Skip to content

Extensions

Extensions are third-party tools -- BOFs, .NET assemblies, DLLs, scripts, and aliases -- that plug into Stentor and register as native shell commands in the beacon console. The extension system provides an Armory-style catalog for browsing, installing, updating, and managing extensions, along with Ed25519 signature verification for supply chain integrity.


Overview

Extensions allow operators to expand Stentor's post-exploitation capabilities without modifying the core codebase. Each extension ships with a manifest that declares its metadata, supported platforms, entry point, and the shell commands it provides. Once installed and enabled, the extension's commands become available in the beacon console alongside built-in commands.

Key capabilities:

  • Catalog browsing -- Discover extensions from a remote repository with one API call
  • One-click install -- Install extensions by name; the server downloads, verifies, and stores them automatically
  • Signature verification -- Ed25519 signatures on extension packages prevent tampering
  • Enable/disable toggle -- Temporarily disable an extension without uninstalling it
  • Automatic updates -- Check for and apply new versions from the catalog
  • Manual upload -- Install extensions from local files for air-gapped environments
  • Command resolution -- Enabled extensions are automatically resolved when operators type their commands in the beacon shell
flowchart LR
    A[Extension Catalog] -->|download + verify| B[Stentor Server]
    C[Manual Upload] -->|multipart POST| B
    B -->|store| D[(PostgreSQL)]
    D -->|resolve command| E[Beacon Shell]
    E -->|execute| F[Beacon]

Extension Types

Extensions are categorized by their runtime type:

Type Description Entry Point
bof Beacon Object File (position-independent C code) C function name (e.g., go)
assembly .NET assembly (execute-assembly) Fully qualified class.method
dll Native DLL (reflective loading) Exported function name
script CNA script extension CNA function name
alias Command alias (maps to existing commands) Alias target

Extension Manifest

Every extension package includes a manifest.json that describes the extension. The manifest is validated on install with the following rules:

  • name must be alphanumeric with dashes/underscores (regex: ^[a-zA-Z0-9][a-zA-Z0-9_-]*$)
  • version must be semver format (e.g., 1.2.0, 1.0.0-beta.1)
  • type must be one of: bof, assembly, dll, script, alias
  • At least one command must be declared
Field Type Required Description
name string Yes Unique extension identifier (e.g., "nanodump")
version string Yes Semantic version (e.g., "1.2.0")
author string Yes Author name or handle
description string Yes Human-readable description
type string Yes Extension type: bof, assembly, dll, script, alias
platforms string[] Yes Supported platforms (e.g., ["windows/x64", "windows/x86"])
entry_point string Yes Runtime entry point (function name, class.method, etc.)
commands ExtensionCommand[] Yes Shell commands this extension provides
dependencies string[] No Other extension names required
min_version string No Minimum Stentor version required
license string No SPDX license identifier
repository string No Source code URL
tags string[] No Category tags for search

Each command in the commands array:

Field Type Required Description
name string Yes Shell command name (e.g., "nanodump")
description string Yes Help text
usage string Yes Usage syntax (e.g., "nanodump [pid] [--fork]")
help_text string No Extended help

Example manifest:

{
  "name": "nanodump",
  "version": "1.3.0",
  "author": "helpsystems",
  "description": "MiniDumpWriteDump via syscalls for LSASS credential extraction",
  "type": "bof",
  "platforms": ["windows/x64"],
  "entry_point": "go",
  "commands": [
    {
      "name": "nanodump",
      "description": "Dump LSASS credentials using NtReadVirtualMemory syscalls",
      "usage": "nanodump [--pid <pid>] [--fork] [--dup] [--write C:\\path]"
    }
  ],
  "min_version": "3.0.0",
  "license": "MIT",
  "tags": ["credentials", "lsass", "bof"]
}

API Reference

All extension endpoints require JWT authentication.

Base path: /api/v1/extensions

List Installed Extensions

GET /api/v1/extensions

Returns all installed extensions, ordered by name. The data and signature fields are excluded for efficiency; the response includes a computed size field instead.

curl -s https://stentor.app/api/v1/extensions \
  -H "Authorization: Bearer $TOKEN"

Response (200 OK):

[
  {
    "id": "d4e5f6a7-b8c9-0123-4567-890abcdef012",
    "name": "nanodump",
    "version": "1.3.0",
    "author": "helpsystems",
    "description": "MiniDumpWriteDump via syscalls for LSASS credential extraction",
    "type": "bof",
    "platforms": ["windows/x64"],
    "entry_point": "go",
    "manifest": { ... },
    "enabled": true,
    "size": 45056,
    "installed_at": "2026-02-20T14:30:00Z"
  }
]

Get Extension

GET /api/v1/extensions/:id

Returns a single extension by ID (includes full manifest).

curl -s https://stentor.app/api/v1/extensions/$EXTENSION_ID \
  -H "Authorization: Bearer $TOKEN"

Browse Catalog

GET /api/v1/extensions/catalog

Fetches the list of available extensions from the configured remote repository. Results are cached server-side for 5 minutes.

curl -s https://stentor.app/api/v1/extensions/catalog \
  -H "Authorization: Bearer $TOKEN"

Response (200 OK):

[
  {
    "name": "nanodump",
    "version": "1.3.0",
    "author": "helpsystems",
    "description": "MiniDumpWriteDump via syscalls for LSASS credential extraction",
    "type": "bof",
    "platforms": ["windows/x64"],
    "entry_point": "go",
    "commands": [
      {
        "name": "nanodump",
        "description": "Dump LSASS credentials using NtReadVirtualMemory syscalls",
        "usage": "nanodump [--pid <pid>] [--fork] [--dup] [--write C:\\path]"
      }
    ]
  }
]

Catalog Configuration

The catalog URL is configured via the EXTENSION_REPO_URL server environment variable. If not set, the catalog endpoint returns an empty array.


Install Extension

POST /api/v1/extensions/install

Downloads and installs an extension from the remote repository. The server fetches the package (tar.gz), downloads and verifies the Ed25519 signature, extracts the embedded manifest, and stores everything in the database.

Request Body:

Field Type Required Description
name string Yes Extension name (must match a catalog entry)
version string No Specific version to install. Omit or "latest" for the newest version.
curl -s -X POST https://stentor.app/api/v1/extensions/install \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "nanodump"}'

Response (201 Created):

{
  "id": "d4e5f6a7-b8c9-0123-4567-890abcdef012",
  "name": "nanodump",
  "version": "1.3.0",
  "author": "helpsystems",
  "description": "MiniDumpWriteDump via syscalls for LSASS credential extraction",
  "type": "bof",
  "platforms": ["windows/x64"],
  "entry_point": "go",
  "manifest": { ... },
  "enabled": true,
  "size": 45056,
  "installed_at": "2026-02-21T10:00:00Z"
}
curl -s -X POST https://stentor.app/api/v1/extensions/install \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "nanodump", "version": "1.2.0"}'

Error Responses:

Status Body Cause
400 {"error": "..."} Invalid request or signature verification failure
404 {"error": "extension \"xyz\" not found in catalog"} Name not in catalog
409 {"error": "extension \"nanodump\" already installed"} Duplicate name

Upload Extension (Manual)

POST /api/v1/extensions/upload

Install an extension from local files using multipart form upload. This is the offline installation path for air-gapped environments.

Multipart Form Fields:

Field Type Required Description
manifest file Yes The manifest.json file
data file Yes The extension binary (BOF .o, .NET assembly, DLL, or script)
signature file No Ed25519 signature file (.sig). If provided, it is verified against the data.
curl -s -X POST https://stentor.app/api/v1/extensions/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]" \
  -F "[email protected]" \
  -F "[email protected]"

Response (201 Created): Same as Install response.

Signature Verification on Upload

If a signature file is provided, the server verifies it against the data bytes using the configured Ed25519 public key. If no public key is configured (development mode), verification is skipped with a warning log.


Remove Extension

DELETE /api/v1/extensions/:id

Uninstalls an extension. Its shell commands will no longer be available in the beacon console.

curl -s -X DELETE https://stentor.app/api/v1/extensions/$EXTENSION_ID \
  -H "Authorization: Bearer $TOKEN"

Response: 204 No Content


Toggle Extension

POST /api/v1/extensions/:id/toggle

Enable or disable an extension without removing it. Disabled extensions remain installed but their commands are not resolved in the beacon shell.

Request Body:

Field Type Required Description
enabled boolean Yes true to enable, false to disable
curl -s -X POST https://stentor.app/api/v1/extensions/$EXTENSION_ID/toggle \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

Response (200 OK): Returns the updated extension object.


Update Extension

POST /api/v1/extensions/:id/update

Updates an installed extension to the latest version from the remote catalog. The server compares the installed version against the catalog, downloads the new package if a newer version exists, verifies the signature, and replaces the stored data.

curl -s -X POST https://stentor.app/api/v1/extensions/$EXTENSION_ID/update \
  -H "Authorization: Bearer $TOKEN"

Response (200 OK):

If an update was applied, returns the updated extension object. If already up to date:

{
  "message": "already up to date",
  "extension": { ... }
}

Signature Verification

Extension packages are signed with Ed25519 to ensure integrity and authenticity. The verification flow works as follows:

sequenceDiagram
    participant Operator
    participant Server
    participant Repository
    Operator->>Server: POST /extensions/install
    Server->>Repository: GET /packages/{name}/{version}.tar.gz
    Repository-->>Server: Package data
    Server->>Repository: GET /packages/{name}/{version}.sig
    Repository-->>Server: Ed25519 signature (64 bytes)
    Server->>Server: Verify signature against data
    alt Signature valid
        Server->>Server: Extract manifest.json from tar.gz
        Server->>Server: Store extension in database
        Server-->>Operator: 201 Created
    else Signature invalid
        Server-->>Operator: 400 Bad Request
    end

Configuration:

The Ed25519 public key is configured via the EXTENSION_PUBLIC_KEY server environment variable (hex-encoded). If not set, the server runs in development mode and skips signature verification with a warning log.

Production Deployment

Always configure EXTENSION_PUBLIC_KEY in production to prevent installation of tampered extension packages. Without it, any package will be accepted regardless of its signature.

Signing extensions (for package authors):

The GenerateKeyPair() and SignExtension() functions in the extension service can be used to create key pairs and sign packages:

  1. Generate an Ed25519 key pair
  2. Sign the extension tar.gz with the private key
  3. Distribute the .sig file alongside the package
  4. Configure the public key on all Stentor servers

Extension Response Object

All API responses (except delete) return the ExtensionResponse format, which excludes the raw binary data and signature bytes for efficiency.

Field Type Description
id string (UUID) Extension ID
name string Extension name
version string Installed version
author string Author
description string Description
type string Extension type (bof, assembly, dll, script, alias)
platforms string[] Supported platforms
entry_point string Runtime entry point
manifest object Full manifest JSON
enabled boolean Whether the extension is active
size integer Extension data size in bytes
installed_at string (ISO 8601) Installation timestamp

Repository Package Format

Extension packages in the catalog repository follow this structure:

/catalog.json                          # Array of ExtensionManifest objects
/packages/{name}/{version}.tar.gz      # Gzipped tar archive containing:
                                       #   manifest.json
                                       #   <extension binary>
/packages/{name}/{version}.sig         # Ed25519 signature of the .tar.gz

The manifest.json can appear at any depth within the tar.gz archive -- the extractor searches for any file ending in manifest.json.


UI Integration

The Extensions page (/extensions) provides three sections:

  • Installed Extensions -- Table view with columns for name, version, type, author, platforms, commands, size, and enabled toggle. Each row has update and remove buttons.
  • Extension Catalog -- Collapsible panel that fetches the remote catalog on demand. Shows available extensions with install buttons. Already-installed extensions show an "Installed" badge.
  • Manual Upload -- Collapsible form for uploading local manifest, data, and optional signature files.

Cross-References

  • REST API Reference -- Extension endpoints are listed under the Extensions section
  • Beacon Commands -- Extensions register as additional beacon shell commands
  • Scripting -- CNA script extensions use the script type