Compare commits

..
3777 changed files with 2023113 additions and 3707333 deletions

No files matched your search

+5 -5
View File
@@ -31,7 +31,7 @@ jobs:
run: make servers
- name: Server Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: servers
path: ./sliver-server_*
@@ -42,10 +42,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Go 1.22
- name: Go 1.21
uses: actions/setup-go@v5
with:
go-version: "^1.22"
go-version: "^1.21"
- name: Check Out Code
uses: actions/checkout@v4
@@ -57,7 +57,7 @@ jobs:
run: make clients
- name: Client Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: clients
path: ./sliver-client_*
@@ -72,7 +72,7 @@ jobs:
steps:
- run: mkdir -p ./builds
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v3
with:
path: ./builds
+4 -4
View File
@@ -16,10 +16,10 @@ jobs:
steps:
# Setup go environment
- name: Go 1.22
- name: Go 1.21
uses: actions/setup-go@v5
with:
go-version: "^1.22"
go-version: "^1.21"
- name: Checkout repository
uses: actions/checkout@v4
@@ -39,7 +39,7 @@ jobs:
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
config-file: ./.github/codeql/codeql-config.yml
languages: go
@@ -48,6 +48,6 @@ jobs:
run: make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2
env:
VERSION: ${{github.sha}}
+5 -5
View File
@@ -35,7 +35,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Detect package manager
id: detect-package-manager
@@ -63,10 +63,10 @@ jobs:
node-version: "lts/*"
- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@v3
- name: Restore cache
uses: actions/cache@v4
uses: actions/cache@v3
with:
path: |
.next/cache
@@ -85,7 +85,7 @@ jobs:
run: ${{ steps.detect-package-manager.outputs.runner }} next build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v1
with:
path: ${{ github.workspace }}/docs/sliver-docs/out
@@ -99,4 +99,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
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
timeout-minutes: 90
steps:
- name: Go 1.22
- name: Go 1.21
uses: actions/setup-go@v5
with:
go-version: "^1.22"
go-version: "^1.21"
- name: OS Packages
run: |
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
uses: actions/checkout@v4
@@ -56,10 +57,10 @@ jobs:
runs-on: macos-latest
timeout-minutes: 90
steps:
- name: Go 1.22
- name: Go 1.21
uses: actions/setup-go@v5
with:
go-version: "^1.22"
go-version: "^1.21"
- name: Mingw
run: brew install mingw-w64
@@ -74,6 +75,9 @@ jobs:
run: make 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
- name: Make MacOS (arm64)
@@ -84,10 +88,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Go 1.22
- name: Go 1.21
uses: actions/setup-go@v5
with:
go-version: "^1.22"
go-version: "^1.21"
- name: Check Out Code
uses: actions/checkout@v4
+12 -7
View File
@@ -9,7 +9,7 @@
# STAGE: base
## Compiles Sliver for use
FROM golang:1.23.5 AS base
FROM --platform=linux/amd64 golang:1.21.4 as base
### Base packages
RUN apt-get update --fix-missing && apt-get -y install \
@@ -29,12 +29,12 @@ RUN cp -vv sliver-server /opt/sliver-server
# STAGE: test
## Run unit tests against the compiled instance
## Use `--target test` in the docker build command to run this stage
FROM base AS test
FROM --platform=linux/amd64 base as test
RUN apt-get update --fix-missing \
&& apt-get -y upgrade \
&& apt-get -y install \
curl
curl gcc-multilib build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64
RUN /opt/sliver-server unpack --force
@@ -43,7 +43,7 @@ RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
# STAGE: production
## Final dockerized form of Sliver
FROM debian:bookworm-slim AS production
FROM --platform=linux/amd64 debian:bookworm-slim as production
### Install production packages
RUN apt-get update --fix-missing \
@@ -56,7 +56,8 @@ RUN apt-get update --fix-missing \
postgresql-contrib postgresql-client libpq-dev \
curl libapr1 libaprutil1 libsvn1 \
libpcap-dev libsqlite3-dev libgmp3-dev \
nasm
mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
nasm gcc-multilib
### Install MSF for stager generation
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)
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
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
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`
**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).
@@ -47,7 +47,7 @@ See the [wiki](https://sliver.sh/docs?name=Compile+from+Source).
### 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
+11 -50
View File
@@ -27,8 +27,7 @@ import (
)
const (
ArmoryConfigFileName = "armories.json"
DefaultArmoryName = "Default"
armoryConfigFileName = "armories.json"
)
var (
@@ -37,11 +36,9 @@ var (
// DefaultArmoryRepoURL - The default repo url for the armory
DefaultArmoryRepoURL string
DefaultArmoryConfig = &ArmoryConfig{
defaultArmoryConfig = &ArmoryConfig{
PublicKey: DefaultArmoryPublicKey,
RepoURL: DefaultArmoryRepoURL,
Name: DefaultArmoryName,
Enabled: true,
}
)
@@ -51,65 +48,29 @@ type ArmoryConfig struct {
RepoURL string `json:"repo_url"`
Authorization string `json:"authorization"`
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
func GetArmoriesConfig() []*ArmoryConfig {
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName)
armoryConfigPath := filepath.Join(GetRootAppDir(), armoryConfigFileName)
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
return []*ArmoryConfig{DefaultArmoryConfig}
return []*ArmoryConfig{defaultArmoryConfig}
}
data, err := os.ReadFile(armoryConfigPath)
if err != nil {
return []*ArmoryConfig{DefaultArmoryConfig}
return []*ArmoryConfig{defaultArmoryConfig}
}
var armoryConfigsFromFile []*ArmoryConfig
var armoryConfigs []*ArmoryConfig
err = json.Unmarshal(data, &armoryConfigsFromFile)
err = json.Unmarshal(data, &armoryConfigs)
if err != nil {
return []*ArmoryConfig{DefaultArmoryConfig}
return []*ArmoryConfig{defaultArmoryConfig}
}
// Force the default armory to be the last
defaultArmorySpecified := false
for _, config := range armoryConfigsFromFile {
if config.Name == DefaultArmoryName {
defaultArmorySpecified = true
continue
} else {
armoryConfigs = append(armoryConfigs, config)
for _, armoryConfig := range armoryConfigs {
if armoryConfig.AuthorizationCmd != "" {
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
}
}
if defaultArmorySpecified {
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
return append(armoryConfigs, defaultArmoryConfig)
}
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
+1 -1
View File
@@ -35,7 +35,7 @@ type NameValueProbability struct {
Name string `json:"name"`
Value string `json:"value"`
Probability int `json:"probability"`
Method string `json:"method"`
Methods []string
}
// HTTPC2ImplantConfig - Implant configuration options
+8 -9
View File
@@ -24,17 +24,20 @@ import (
"os"
"path"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/version"
)
const (
logFileName = "sliver-client.log"
)
// Initialize logging.
var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
// Initialize logging
func initLogging(appDir string) *os.File {
log.SetFlags(log.LstdFlags | log.Lshortfile)
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() {
appDir := assets.GetRootAppDir()
logFile := initLogging(appDir)
defer logFile.Close()
rootCmd.TraverseChildren = true
// Create the console client, without any RPC or commands bound to it yet.
@@ -89,10 +88,10 @@ var rootCmd = &cobra.Command{
Long: ``,
}
// Execute - Execute root command.
// Execute - Execute root command
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Printf("root command: %s\n", err)
fmt.Println(err)
os.Exit(1)
}
}
+1
View File
@@ -23,6 +23,7 @@ import (
"sort"
"github.com/bishopfox/sliver/client/assets"
"gopkg.in/AlecAivazis/survey.v1"
)
+10 -42
View File
@@ -1,53 +1,37 @@
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 (
"context"
"fmt"
"os"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/transport"
"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.
func consoleCmd(con *console.SliverClient) *cobra.Command {
// consoleCmd generates the console with required pre/post runners
func consoleCmd(con *console.SliverConsoleClient) *cobra.Command {
consoleCmd := &cobra.Command{
Use: "console",
Short: "Start the sliver client console",
}
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
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
pre = func(_ *cobra.Command, _ []string) error {
appDir := assets.GetRootAppDir()
logFile := initLogging(appDir)
defer logFile.Close()
configs := assets.GetConfigs()
if len(configs) == 0 {
@@ -73,9 +57,6 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
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)
}
@@ -90,16 +71,3 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
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
/*
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 (
"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/use"
"github.com/bishopfox/sliver/client/console"
"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
makeCommands := command.SliverCommands(con)
@@ -42,7 +25,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
implantFlags.StringP("use", "s", "", "interact with a session")
cmd.Flags().AddFlagSet(implantFlags)
// Pre-runners (console setup, connection, etc)
// Prerunners (console setup, connection, etc)
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
// Completions
@@ -51,7 +34,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
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)
// 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
}
func makeCompleters(cmd *cobra.Command, con *console.SliverClient) {
func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) {
comps := carapace.Gen(cmd)
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)
command.BindFlagCompletions(cmd, func(comp *carapace.ActionMap) {
command.FlagComps(cmd, func(comp *carapace.ActionMap) {
(*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
cmd.PersistentPreRunE(cmd, c.Args)
return use.SessionIDCompleter(con)
+2 -1
View File
@@ -22,9 +22,10 @@ import (
"fmt"
"os"
"github.com/bishopfox/sliver/client/assets"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
)
func importCmd() *cobra.Command {
+2 -1
View File
@@ -21,8 +21,9 @@ package cli
import (
"fmt"
"github.com/bishopfox/sliver/client/version"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/version"
)
var cmdVersion = &cobra.Command{
+8 -7
View File
@@ -27,14 +27,15 @@ import (
"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/text"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
)
// AliasesCmd - The alias command.
func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) error {
// AliasesCmd - The alias command
func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) error {
if 0 < len(loadedAliases) {
PrintAliases(con)
} else {
@@ -44,8 +45,8 @@ func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) er
return nil
}
// PrintAliases - Print a list of loaded aliases.
func PrintAliases(con *console.SliverClient) {
// PrintAliases - Print a list of loaded aliases
func PrintAliases(con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
@@ -87,8 +88,8 @@ func PrintAliases(con *console.SliverClient) {
con.Println(tw.Render())
}
// AliasCommandNameCompleter - Completer for installed extensions command names.
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverClient) []string {
// AliasCommandNameCompleter - Completer for installed extensions command names
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
results := []string{}
for name := range loadedAliases {
if strings.HasPrefix(name, prefix) {
@@ -128,7 +129,7 @@ func getInstalledManifests() map[string]*AliasManifest {
return installedManifests
}
// AliasCommandNameCompleter - Completer for installed extensions command names.
// AliasCommandNameCompleter - Completer for installed extensions command names
func AliasCompleter() carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
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"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
// AliasesInstallCmd - Install an alias.
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// AliasesInstallCmd - Install an alias
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
aliasLocalPath := args[0]
fi, err := os.Stat(aliasLocalPath)
if os.IsNotExist(err) {
con.PrintErrorf("Alias path '%s' does not exist", aliasLocalPath)
con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
return
}
if !fi.IsDir() {
InstallFromFile(aliasLocalPath, "", false, con)
InstallFromFile(aliasLocalPath, false, con)
} else {
installFromDir(aliasLocalPath, con)
}
}
// Install an extension from a directory.
func installFromDir(aliasLocalPath string, con *console.SliverClient) {
// Install an extension from a directory
func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
if err != nil {
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
@@ -99,8 +100,8 @@ func installFromDir(aliasLocalPath string, con *console.SliverClient) {
con.Printf("done!\n")
}
// Install an extension from a .tar.gz file.
func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite bool, con *console.SliverClient) *string {
// Install an extension from a .tar.gz file
func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName))
if err != nil {
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)
if err != nil {
errorMsg := ""
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)
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
return nil
}
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
if promptToOverwrite {
if !autoOverwrite {
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
@@ -145,7 +140,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
}
for _, aliasFile := range manifest.Files {
if aliasFile.Path != "" {
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path)
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
if err != nil {
con.PrintErrorf("Failed to install file: %s\n", err)
forceRemoveAll(installPath)
@@ -157,7 +152,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
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))))
if err != nil {
return err
+54 -108
View File
@@ -28,18 +28,18 @@ import (
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/packages"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
app "github.com/reeflective/console"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"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 (
@@ -53,30 +53,30 @@ const (
)
var (
// alias name -> manifest/command.
// alias name -> manifest/command
loadedAliases = map[string]*loadedAlias{}
defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": linuxDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
)
// Ties the manifest struct to the command struct.
// Ties the manifest struct to the command struct
type loadedAlias struct {
Manifest *AliasManifest
Command *cobra.Command
}
// AliasFile - An OS/Arch specific file.
// AliasFile - An OS/Arch specific file
type AliasFile struct {
OS string `json:"os"`
Arch string `json:"arch"`
Path string `json:"path"`
}
// AliasManifest - The manifest for an alias, contains metadata.
// AliasManifest - The manifest for an alias, contains metadata
type AliasManifest struct {
Name string `json:"name"`
Version string `json:"version"`
@@ -86,17 +86,14 @@ type AliasManifest struct {
Help string `json:"help"`
LongHelp string `json:"long_help"`
Entrypoint string `json:"entrypoint"`
AllowArgs bool `json:"allow_args"`
DefaultArgs string `json:"default_args"`
Files []*AliasFile `json:"files"`
IsReflective bool `json:"is_reflective"`
IsAssembly bool `json:"is_assembly"`
Schema *packages.OutputSchema `json:"schema"`
Entrypoint string `json:"entrypoint"`
AllowArgs bool `json:"allow_args"`
DefaultArgs string `json:"default_args"`
Files []*AliasFile `json:"files"`
IsReflective bool `json:"is_reflective"`
IsAssembly bool `json:"is_assembly"`
RootPath string `json:"-"`
ArmoryName string `json:"-"`
ArmoryPK string `json:"-"`
RootPath string `json:"-"`
}
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
}
func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch string) (string, error) {
func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
filePath := ""
for _, extFile := range a.Files {
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.
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
dirPath := args[0]
// dirPath := ctx.Args.String("dir-path")
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.
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClient) (*AliasManifest, error) {
// LoadAlias - Load an alias into the Sliver shell from a given directory
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConsoleClient) (*AliasManifest, error) {
// retrieve alias manifest
var err error
manifestPath, err = filepath.Abs(manifestPath)
@@ -208,7 +205,7 @@ func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClien
return aliasManifest, nil
}
// ParseAliasManifest - Parse an alias manifest.
// ParseAliasManifest - Parse an alias manifest
func ParseAliasManifest(data []byte) (*AliasManifest, error) {
// parse it
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
}
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()
if session == nil && beacon == nil {
return
@@ -278,20 +268,20 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
return
}
// args := ctx.Args.StringList("arguments")
var extArgsStr string
var extArgs string
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
extArgsStr = aliasManifest.DefaultArgs
extArgs = aliasManifest.DefaultArgs
} else {
extArgsStr = strings.Join(args, " ")
extArgs = strings.Join(args, " ")
}
extArgsStr = strings.TrimSpace(extArgsStr)
extArgs = strings.TrimSpace(extArgs)
entryPoint := aliasManifest.Entrypoint
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
// 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
// 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 := ""
// 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
@@ -363,13 +353,13 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
// Execute Assembly
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)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(cmd),
IsDLL: isDLL,
Process: processName,
Arguments: args,
Arguments: extArgs,
Assembly: binData,
Arch: arch,
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)
return
}
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con)
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
})
con.PrintAsyncResponse(executeAssemblyResp.Response)
} else {
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con)
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
}
} else if aliasManifest.IsReflective {
@@ -409,11 +399,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
// Spawn DLL
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)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveTarget.Request(cmd),
Args: args,
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
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)
return
}
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con)
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
})
con.PrintAsyncResponse(spawnDllResp.Response)
} else {
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con)
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
}
} else {
@@ -448,11 +438,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
// Sideload
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)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveTarget.Request(cmd),
Args: args,
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
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)
return
}
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con)
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
})
con.PrintAsyncResponse(sideloadResp.Response)
} else {
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con)
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
}
}
}
func getOutputWithSchema(schema *packages.OutputSchema, result string) string {
if schema == nil {
return result
}
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
// PrintSpawnDLLOutput - Prints the output of a spawn dll command
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.WriteString(spawnDllResp.GetResult())
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
// PrintSideloadOutput - Prints the output of a sideload command.
func PrintSideloadOutput(cmdName string, schema *packages.OutputSchema, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverClient) {
var result string
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
// PrintSideloadOutput - Prints the output of a sideload command
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.WriteString(sideloadResp.GetResult())
outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
// 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) {
var result string
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
// PrintAssemblyOutput - Prints the output of an execute-assembly command
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) {
con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(execAsmResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
+5 -4
View File
@@ -25,13 +25,14 @@ import (
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/spf13/cobra"
)
// 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 := ctx.Args.String("name")
if name == "" {
@@ -53,8 +54,8 @@ func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
}
}
// RemoveAliasByCommandName - Remove an alias by command name.
func RemoveAliasByCommandName(commandName string, con *console.SliverClient) error {
// RemoveAliasByCommandName - Remove an alias by command name
func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClient) error {
if commandName == "" {
return errors.New("command name is required")
}
+74 -364
View File
@@ -23,7 +23,6 @@ import (
"errors"
"fmt"
"net/url"
"slices"
"sync"
"time"
@@ -55,24 +54,13 @@ type ArmoryPackage struct {
RepoURL string `json:"repo_url"`
PublicKey string `json:"public_key"`
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:"-"`
IsAlias bool `json:"-"`
}
// ArmoryBundle - A list of packages
type ArmoryBundle struct {
Name string `json:"name"`
Packages []string `json:"packages"`
ArmoryName string `json:"-"`
Name string `json:"name"`
Packages []string `json:"packages"`
}
// ArmoryHTTPConfig - Configuration for armory HTTP client
@@ -101,75 +89,48 @@ type pkgCacheEntry struct {
Alias *alias.AliasManifest
Extension *extensions.ExtensionManifest
LastErr error
// This corresponds to Pkg.ID
ID string
}
var (
// public key -> armoryCacheEntry
indexCache = sync.Map{}
// package ID -> armoryPkgCacheEntry
// public key -> armoryPkgCacheEntry
pkgCache = sync.Map{}
// public key -> assets.ArmoryConfig
currentArmories = sync.Map{}
// cacheTime - How long to cache the index/pkg manifests
//cacheTime = time.Hour
cacheTime = 2 * time.Minute
cacheTime = time.Hour
// This will kill a download if exceeded so needs to be large
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
func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
armoriesConfig := getCurrentArmoryConfiguration()
if len(armoriesConfig) == 1 {
con.Printf("Reading armory index ... ")
} else {
con.PrintInfof("Reading %d armory indexes ... ", len(armoriesConfig))
}
func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
armoriesConfig := assets.GetArmoriesConfig()
con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
clientConfig := parseArmoryHTTPConfig(cmd)
indexes := fetchIndexes(clientConfig)
indexes := fetchIndexes(armoriesConfig, clientConfig)
if len(indexes) != len(armoriesConfig) {
con.Printf("errors!\n")
indexCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(indexCacheEntry)
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
})
} else {
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
con.PrintInfof("Reading package information for armory %s ... ", index.ArmoryConfig.Name)
fetchPackageSignatures(index, clientConfig)
aliases := []*alias.AliasManifest{}
exts := []*extensions.ExtensionManifest{}
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry, ok := value.(pkgCacheEntry)
if !ok {
// Something is wrong with this entry
pkgCache.Delete(value)
return true
}
if cacheEntry.ArmoryConfig.PublicKey != index.ArmoryConfig.PublicKey {
return true
}
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.LastErr != nil {
errorCount++
if errorCount == 0 {
@@ -188,52 +149,32 @@ func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
if errorCount == 0 {
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()
PrintArmoryPackages(aliases, exts, con)
bundles := bundlesInCache()
if 0 < len(bundles) {
PrintArmoryBundles(bundles, con)
} else {
con.PrintInfof("No bundles found\n")
}
} else {
con.PrintInfof("No packages found\n")
}
con.Println()
bundles := bundlesInCache()
if 0 < len(bundles) {
PrintArmoryBundles(bundles, con)
} else {
con.PrintInfof("No bundles found\n")
con.PrintInfof("No indexes found\n")
}
}
func refresh(clientConfig ArmoryHTTPConfig) {
getCurrentArmoryConfiguration()
indexes := fetchIndexes(clientConfig)
for _, index := range indexes {
fetchPackageSignatures(index, clientConfig)
}
armoriesConfig := assets.GetArmoriesConfig()
indexes := fetchIndexes(armoriesConfig, clientConfig)
fetchPackageSignatures(indexes, clientConfig)
}
func countUniqueCommandsFromManifests(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest) (int, int) {
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) {
func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
aliases := []*alias.AliasManifest{}
exts := []*extensions.ExtensionManifest{}
pkgCache.Range(func(key, value interface{}) bool {
@@ -250,125 +191,6 @@ func packageManifestsInCache() ([]*alias.AliasManifest, []*extensions.ExtensionM
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 {
bundles := []*ArmoryBundle{}
indexCache.Range(func(key, value interface{}) bool {
@@ -385,7 +207,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
var action carapace.Action
results := []string{}
aliases, exts := packageManifestsInCache()
aliases, exts := packagesInCache()
bundles := bundlesInCache()
for _, aliasPkg := range aliases {
@@ -420,7 +242,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
}
// 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)
if err != nil {
width = 1
@@ -434,7 +256,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
if con.Settings.SmallTermWidth+urlMargin < width {
tw.AppendHeader(table.Row{
"Armory",
"Command Name",
"Version",
"Type",
@@ -443,7 +264,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
})
} else {
tw.AppendHeader(table.Row{
"Armory",
"Command Name",
"Version",
"Type",
@@ -453,11 +273,10 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
// Columns start at 1 for some dumb reason
tw.SortBy([]table.SortBy{
{Number: 2, Mode: table.Asc},
{Number: 1, Mode: table.Asc},
})
type pkgInfo struct {
ArmoryName string
CommandName string
Version string
Type string
@@ -465,10 +284,8 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
URL string
}
entries := []pkgInfo{}
for _, aliasPkg := range aliases {
entries = append(entries, pkgInfo{
ArmoryName: aliasPkg.ArmoryName,
CommandName: aliasPkg.CommandName,
Version: aliasPkg.Version,
Type: "Alias",
@@ -479,7 +296,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
for _, extm := range exts {
for _, extension := range extm.ExtCommand {
entries = append(entries, pkgInfo{
ArmoryName: extm.ArmoryName,
CommandName: extension.CommandName,
Version: extension.Manifest.Version,
Type: "Extension",
@@ -499,7 +315,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
}
if con.Settings.SmallTermWidth+urlMargin < width {
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.Version),
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
@@ -508,7 +323,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
})
} else {
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.Version),
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
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Bundles" + console.Normal)
tw.AppendHeader(table.Row{
"Armory Name",
"Name",
"Contains",
})
@@ -552,7 +365,6 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
}
}
tw.AppendRow(table.Row{
bundle.ArmoryName,
bundle.Name,
packages,
})
@@ -590,19 +402,12 @@ func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig {
// fetch armory indexes, only returns indexes that were fetched successfully
// 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{}
// Try to get a max of 10 indexes at a time
currentRequests := make(chan struct{}, 10)
currentArmories.Range(func(key, value interface{}) bool {
armoryEntry := value.(assets.ArmoryConfig)
if armoryEntry.Enabled {
wg.Add(1)
currentRequests <- struct{}{}
go fetchIndex(&armoryEntry, currentRequests, clientConfig, wg)
}
return true
})
for _, armoryConfig := range armoryConfigs {
wg.Add(1)
go fetchIndex(armoryConfig, clientConfig, wg)
}
wg.Wait()
indexes := []ArmoryIndex{}
indexCache.Range(func(key, value interface{}) bool {
@@ -615,19 +420,13 @@ func fetchIndexes(clientConfig ArmoryHTTPConfig) []ArmoryIndex {
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 func() {
<-requestChannel
}()
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
if ok {
cached := cacheEntry.(indexCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
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 {
result := []string{}
for _, pkg := range index.Aliases {
result = append(result, calculatePackageHash(pkg))
}
for _, pkg := range index.Extensions {
result = append(result, calculatePackageHash(pkg))
}
return result
}
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)
}
}
func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig) {
wg := &sync.WaitGroup{}
for _, index := range indexes {
for _, armoryPkg := range index.Extensions {
wg.Add(1)
armoryPkg.IsAlias = false
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
for _, armoryPkg := range index.Aliases {
wg.Add(1)
armoryPkg.IsAlias = true
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
}
// 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()
// 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 func() {
<-requestChannel
}()
cacheEntry, ok := pkgCache.Load(armoryPkg.ID)
cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
if ok {
cached := cacheEntry.(pkgCacheEntry)
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
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{
ArmoryConfig: armoryConfig,
RepoURL: armoryPkg.RepoURL,
ID: armoryPkg.ID,
}
defer func() {
pkgCacheEntry.Fetched = time.Now()
pkgCache.Store(armoryPkg.ID, *pkgCacheEntry)
pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
}()
repoURL, err := url.Parse(armoryPkg.RepoURL)
@@ -784,53 +528,19 @@ func fetchPackageSignature(wg *sync.WaitGroup, requestChannel chan struct{}, arm
if armoryPkg != nil {
pkgCacheEntry.Pkg = *armoryPkg
}
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
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
if err == nil {
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
return
}
return true
})
return armoryPK
if armoryPkg.IsAlias {
pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
} else {
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 (
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"slices"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/util"
"github.com/bishopfox/sliver/util/minisign"
)
// ErrPackageNotFound - The package was 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
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
var promptToOverwrite bool
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name := args[0]
// name := ctx.Args.String("name")
if name == "" {
con.PrintErrorf("A package or bundle name is required")
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)
refresh(clientConfig)
if name == "all" {
aliases, extensions := packageManifestsInCache()
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
aliases, extensions := packagesInCache()
confirm := false
pluralAliases := "es"
if aliasCount == 1 {
pluralAliases = ""
}
pluralExtensions := "s"
if extCount == 1 {
pluralExtensions = ""
}
prompt := &survey.Confirm{
Message: fmt.Sprintf("Install %d alias%s and %d extension%s?",
aliasCount, pluralAliases, extCount, pluralExtensions,
Message: fmt.Sprintf("Install %d aliases and %d extensions?",
len(aliases), len(extensions),
),
}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
promptToOverwrite = false
}
err = installPackageByName(name, armoryPK, forceInstallation, promptToOverwrite, clientConfig, con)
err := installPackageByName(name, clientConfig, con)
if err == nil {
return
}
if errors.Is(err, ErrPackageNotFound) {
if err == ErrPackageNotFound {
bundles := bundlesInCache()
for _, bundle := range bundles {
if bundle.Name == name {
installBundle(bundle, armoryPK, forceInstallation, 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)
installBundle(bundle, clientConfig, con)
return
}
}
}
con.PrintErrorf("No package or bundle named '%s' was found", name)
}
func getInstalledPackageNames() []string {
packageNames := []string{}
installedAliases := assets.GetInstalledAliasManifests()
installedExtensions := assets.GetInstalledExtensionManifests()
for _, aliasFileName := range installedAliases {
alias := &alias.AliasManifest{}
manifestData, err := os.ReadFile(aliasFileName)
func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
for _, pkgName := range bundle.Packages {
err := installPackageByName(pkgName, clientConfig, con)
if err != nil {
continue
}
err = json.Unmarshal(manifestData, alias)
if err != nil {
continue
}
if !slices.Contains(packageNames, alias.CommandName) {
packageNames = append(packageNames, alias.CommandName)
con.PrintErrorf("Failed to install '%s': %s", pkgName, err)
}
}
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 getCommandsInCache() []string {
commandNames := []string{}
pkgCache.Range(func(key, value interface{}) bool {
cacheEntry := value.(pkgCacheEntry)
if cacheEntry.LastErr == 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)
}
}
func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
aliases, extensions := packagesInCache()
for _, alias := range aliases {
if alias.CommandName == name || name == "all" {
installAlias(alias, clientConfig, con)
if name != "all" {
return nil
}
}
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 _, packageName := range requestedPackageList {
if _, ok := pendingPackages[packageName]; ok {
// We are already going to install a package with this name, so do not try to resolve it
continue
}
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
for _, extm := range extensions {
for _, ext := range extm.ExtCommand {
if ext.CommandName == name || name == "all" {
installExtension(ext.Manifest, clientConfig, con)
if name != "all" {
return nil
}
}
}
}
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" {
con.Printf("\n")
con.PrintInfof("Operation complete\n")
con.PrintInfof("All packages installed\n")
return nil
}
return nil
return ErrPackageNotFound
}
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 {
return errors.New("package not found")
}
if !entry.Pkg.IsAlias {
return errors.New("package is not an alias")
}
repoURL, err := url.Parse(entry.RepoURL)
if err != nil {
return err
@@ -496,7 +174,7 @@ func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientCon
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 {
return errors.New("failed to install alias")
}
@@ -510,15 +188,50 @@ func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientCon
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
func resolveExtensionPackageDependencies(pkg *pkgCacheEntry, deps map[string]*pkgCacheEntry, pendingPackages map[string]string) error {
for _, multiExt := range pkg.Extension.ExtCommand {
func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
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 == "" {
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
}
// 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 {
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) {
continue
}
// Figure out what package we need for the dependency
dependencyEntry, err := getPackageForCommand(multiExt.DependsOn, "", "")
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
}
deps[multiExt.DependsOn] = struct{}{}
resolveExtensionPackageDependencies(multiExt.DependsOn, deps, clientConfig, con)
}
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 {
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
extensions.InstallFromDir(tmpFile.Name(), promptToOverwrite, con, true)
extensions.InstallFromDir(tmpFile.Name(), con, true)
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 (
"bytes"
"crypto/sha256"
"crypto/tls"
"encoding/base64"
"encoding/json"
@@ -69,18 +67,6 @@ type armoryPkgResponse struct {
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
//
@@ -93,10 +79,7 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
return nil, err
}
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
if err != nil {
return nil, err
}
resp, body, _ := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
if resp.StatusCode != http.StatusOK {
return nil, errors.New("api returned non-200 status code")
}
@@ -128,45 +111,9 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
if err != nil {
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
}
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
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
var publicKey minisign.PublicKey
@@ -175,29 +122,25 @@ func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *Armory
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 {
return nil, nil, err
}
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{}
err = json.Unmarshal(body, pkgResp)
if err != nil {
return nil, nil, err
}
decodedSig, err := decodePackageSignature(pkgResp.Minisig)
if err != nil {
return nil, nil, err
}
sig, err := parsePkgMinsig(decodedSig)
sig, err := parsePkgMinsig([]byte(pkgResp.Minisig))
if err != nil {
return nil, nil, err
}
var tarGz []byte
if !sigOnly {
tarGzURL, err := url.Parse(pkgResp.TarGzURL)
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
if err != nil {
return nil, nil, err
}
@@ -299,20 +242,6 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
if err != nil {
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
}
@@ -507,12 +436,6 @@ func downloadRequest(clientConfig ArmoryHTTPConfig, reqURL string, armoryConfig
"Accept": {"application/octet-stream"},
}
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 {
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
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 ... ")
clientConfig := parseArmoryHTTPConfig(cmd)
refresh(clientConfig)
@@ -47,7 +47,7 @@ func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
return
}
aliases, exts := packageManifestsInCache()
aliases, exts := packagesInCache()
matchedAliases := []*alias.AliasManifest{}
for _, a := range aliases {
if nameExpr.MatchString(a.CommandName) {
+34 -294
View File
@@ -19,127 +19,56 @@ package armory
*/
import (
"fmt"
"os"
"sort"
"strconv"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
)
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
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
var selectedUpdates []UpdateIdentifier
var err error
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
con.PrintInfof("Refreshing package cache ... ")
clientConfig := parseArmoryHTTPConfig(cmd)
refresh(clientConfig)
con.Printf(console.Clearln + "\r")
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)
}
// 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
// Aliases
aliasUpdates := checkForAliasUpdates(clientConfig, con)
if 0 < len(aliasUpdates) {
con.PrintInfof("%d alias(es) out of date: %s\n", len(aliasUpdates), strings.Join(aliasUpdates, ", "))
for _, aliasName := range aliasUpdates {
err := installAliasPackageByName(aliasName, clientConfig, con)
if err != nil {
con.PrintErrorf("Failed to update %s: %s\n", aliasName, err)
}
}
} else {
con.PrintSuccessf("All packages are up to date")
return
con.PrintInfof("All aliases up to date!\n")
}
for _, update := range selectedUpdates {
switch update.Type {
case AliasPackage:
aliasVersionInfo, ok := aliasUpdates[update.Name]
if !ok {
continue
}
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, aliasVersionInfo.NewVersion)
// Extensions
extUpdates := checkForExtensionUpdates(clientConfig, con)
if 0 < len(extUpdates) {
con.PrintInfof("%d extension(s) out of date: %s\n", len(extUpdates), strings.Join(extUpdates, ", "))
for _, extName := range extUpdates {
err := installExtensionPackageByName(extName, clientConfig, con)
if err != nil {
con.PrintErrorf("Could not get update package for alias %s: %s\n", update.Name, err)
// Keep going because other packages might not encounter errors
continue
con.PrintErrorf("Failed to update %s: %s\n", extName, err)
}
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 {
cachedAliases, _ := packageManifestsInCache()
results := make(map[string]VersionInformation)
func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
cachedAliases, _ := packagesInCache()
results := []string{}
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
data, err := os.ReadFile(aliasManifestPath)
if err != nil {
@@ -150,31 +79,19 @@ func checkForAliasUpdates(armoryPK string) map[string]VersionInformation {
continue
}
for _, latestAlias := range cachedAliases {
/*
We used to check if the version identifiers were different between the installed version and the
version in the armory. This worked when we had one armory, but with multiple potential armories,
we have to have some type of versioning. We do not have an official versioning scheme for packages,
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,
}
}
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
// the armory differs at all from the local version, the extension is out of date.
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version != localManifest.Version {
results = append(results, localManifest.CommandName)
}
}
}
return results
}
func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation {
_, cachedExtensions := packageManifestsInCache()
// Return a map of extension name to minimum version to install
results := make(map[string]VersionInformation)
func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
_, cachedExtensions := packagesInCache()
results := []string{}
for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
data, err := os.ReadFile(extManifestPath)
if err != nil {
@@ -185,190 +102,13 @@ func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation {
continue
}
for _, latestExt := range cachedExtensions {
/*
We used to check if the version identifiers were different between the installed version and the
version in the armory. This worked when we had one armory, but with multiple potential armories,
we have to have some type of versioning. We do not have an official versioning scheme for packages,
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,
}
}
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
// the armory differs at all from the local version, the extension is out of date.
if latestExt.Name == localManifest.Name && latestExt.Version != localManifest.Version {
results = append(results, localManifest.Name)
}
}
}
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 (
"fmt"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/spf13/cobra"
)
// BackdoorCmd - Command to inject implant code into an existing binary.
func BackdoorCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// BackdoorCmd - Command to inject implant code into an existing binary
func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
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
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")
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
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 {
con.PrintInfof("No beacons 🙁\n")
return
@@ -103,7 +103,7 @@ func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp
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)
if err != nil {
width = 999
@@ -112,45 +112,20 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
wideTermWidth := con.Settings.SmallTermWidth < width
windowsBeaconInList := false
for _, beacon := range beacons {
if beacon.OS == "windows" {
windowsBeaconInList = true
}
}
if wideTermWidth {
if windowsBeaconInList {
tw.AppendHeader(table.Row{
"ID",
"Name",
"Tasks",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Process (PID)",
"Integrity",
"Operating System",
"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",
})
}
tw.AppendHeader(table.Row{
"ID",
"Name",
"Tasks",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Locale",
"Last Check-in",
"Next Check-in",
})
} else {
tw.AppendHeader(table.Row{
"ID",
@@ -170,9 +145,6 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
color = console.Green
}
if beacon.Integrity == "" {
beacon.Integrity = "-"
}
// We need a slice of strings so we can apply filters
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.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"+console.Normal, beacon.Locale),
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true),
}...)
}
} else {
rowEntries = []string{
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
func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
grpcCtx, cancel := con.GrpcContext(nil)
defer cancel()
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
@@ -102,7 +102,7 @@ func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
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)
defer cancel()
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
}
func GetBeacons(con *console.SliverClient) (*clientpb.Beacons, error) {
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
grpcCtx, cancel := con.GrpcContext(nil)
defer cancel()
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
+1 -1
View File
@@ -30,7 +30,7 @@ import (
)
// 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")
pruneDuration, err := time.ParseDuration(duration)
if err != nil {
+1 -1
View File
@@ -25,7 +25,7 @@ import (
)
// 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)
if err != nil {
con.PrintErrorf("%s\n", err)
+1 -1
View File
@@ -31,7 +31,7 @@ import (
)
// 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()
defer func() {
con.Printf(console.UpN+console.Clearln+"\r", 1)
+6 -5
View File
@@ -23,16 +23,17 @@ import (
"fmt"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
// BuildersCmd - List external builders.
func BuildersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// BuildersCmd - List external builders
func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
if err != nil {
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.SetStyle(settings.GetTableStyle(con))
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}
}
+13 -400
View File
@@ -21,13 +21,8 @@ package c2profiles
import (
"context"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
@@ -44,7 +39,7 @@ import (
)
// 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")
if profileName == constants.DefaultC2Profile {
@@ -66,7 +61,7 @@ func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
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")
if profileName == "" {
@@ -128,260 +123,6 @@ func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []st
}
}
func ExportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
filepath, _ := cmd.Flags().GetString("file")
if filepath == "" {
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
}
c2Profiles, err := con.Rpc.GetHTTPC2Profiles(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
var extensions []string
for _, c2profile := range c2Profiles.Configs {
confProfile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: c2profile.Name})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
extensions = append(extensions, confProfile.ImplantConfig.StagerFileExtension)
extensions = append(extensions, confProfile.ImplantConfig.StartSessionFileExtension)
}
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(extensions, 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 {
con.PrintErrorf("%s\n", err)
return
}
con.Println("C2 profile generated and saved as ", profileName)
} else {
PrintC2Profiles(profile, con)
}
}
// convert protobuf to json
func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) (*assets.HTTPC2Config, error) {
implantConfig := assets.HTTPC2ImplantConfig{
UserAgent: profile.ImplantConfig.UserAgent,
ChromeBaseVersion: int(profile.ImplantConfig.ChromeBaseVersion),
MacOSVersion: profile.ImplantConfig.MacOSVersion,
NonceQueryArgChars: profile.ImplantConfig.NonceQueryArgChars,
MaxFiles: int(profile.ImplantConfig.MaxFiles),
MinFiles: int(profile.ImplantConfig.MinFiles),
MaxPaths: int(profile.ImplantConfig.MaxPaths),
MinPaths: int(profile.ImplantConfig.MinFiles),
StagerFileExt: profile.ImplantConfig.StagerFileExtension,
PollFileExt: profile.ImplantConfig.PollFileExtension,
StartSessionFileExt: profile.ImplantConfig.StartSessionFileExtension,
SessionFileExt: profile.ImplantConfig.SessionFileExtension,
CloseFileExt: profile.ImplantConfig.CloseFileExtension,
}
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 (
stagerFiles []string
pollFiles []string
sessionFiles []string
closeFiles []string
stagerPaths []string
pollPaths []string
sessionPaths []string
closePaths []string
)
for _, pathSegment := range profile.ImplantConfig.PathSegments {
if pathSegment.IsFile {
switch pathSegment.SegmentType {
case 0:
pollFiles = append(pollFiles, pathSegment.Value)
case 1:
sessionFiles = append(sessionFiles, pathSegment.Value)
case 2:
closeFiles = append(closeFiles, pathSegment.Value)
case 3:
stagerFiles = append(stagerFiles, pathSegment.Value)
}
} else {
switch pathSegment.SegmentType {
case 0:
pollPaths = append(pollPaths, pathSegment.Value)
case 1:
sessionPaths = append(sessionPaths, pathSegment.Value)
case 2:
closePaths = append(closePaths, pathSegment.Value)
case 3:
stagerPaths = append(stagerPaths, pathSegment.Value)
}
}
}
implantConfig.PollFiles = pollFiles
implantConfig.SessionFiles = sessionFiles
implantConfig.CloseFiles = closeFiles
implantConfig.StagerFiles = stagerFiles
implantConfig.PollPaths = pollPaths
implantConfig.SessionPaths = sessionPaths
implantConfig.ClosePaths = closePaths
implantConfig.StagerPaths = stagerPaths
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
func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *clientpb.HTTPC2Config {
@@ -455,24 +196,6 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
})
}
for _, clientHeader := range config.ImplantConfig.Headers {
httpC2Headers = append(httpC2Headers, &clientpb.HTTPC2Header{
Method: clientHeader.Method,
Name: clientHeader.Name,
Value: clientHeader.Value,
Probability: int32(clientHeader.Probability),
})
}
for _, urlParameter := range config.ImplantConfig.URLParameters {
httpC2UrlParameters = append(httpC2UrlParameters, &clientpb.HTTPC2URLParameter{
Method: urlParameter.Method,
Name: urlParameter.Name,
Value: urlParameter.Value,
Probability: int32(urlParameter.Probability),
})
}
implantConfig := &clientpb.HTTPC2ImplantConfig{
UserAgent: config.ImplantConfig.UserAgent,
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
@@ -495,12 +218,14 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
// Server Config
serverHeaders := []*clientpb.HTTPC2Header{}
for _, serverHeader := range config.ServerConfig.Headers {
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
Method: serverHeader.Method,
Name: serverHeader.Name,
Value: serverHeader.Value,
Probability: int32(serverHeader.Probability),
})
for _, method := range serverHeader.Methods {
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
Method: method,
Name: serverHeader.Name,
Value: serverHeader.Value,
Probability: int32(serverHeader.Probability),
})
}
}
serverCookies := []*clientpb.HTTPC2Cookie{}
@@ -523,7 +248,7 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
}
// 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.SetStyle(settings.GetTableStyle(con))
@@ -543,7 +268,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
var serverHeaders []string
for _, header := range profile.ServerConfig.Headers {
serverHeaders = append(serverHeaders, header.Name)
serverHeaders = append(serverHeaders, header.Value)
}
tw.AppendRow(table.Row{
"Server Headers",
@@ -568,7 +293,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
var clientHeaders []string
for _, header := range profile.ImplantConfig.Headers {
clientHeaders = append(clientHeaders, header.Name)
clientHeaders = append(clientHeaders, header.Value)
}
tw.AppendRow(table.Row{
"Client Headers",
@@ -710,115 +435,3 @@ func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
return c2profile
}
func updateC2Profile(usedExtensions []string, template *assets.HTTPC2Config, urls []string) (*assets.HTTPC2Config, error) {
// update the template with the urls
var (
paths []string
filenames []string
extensions []string
filteredExtensions []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)
for _, extension := range extensions {
if !slices.Contains(usedExtensions, extension) {
filteredExtensions = append(filteredExtensions, extension)
}
}
slices.Sort(paths)
paths = slices.Compact(paths)
slices.Sort(filenames)
filenames = slices.Compact(filenames)
// 5 is arbitrarily used as a minimum value, it only has to be 5 for the extensions, the others can be lower
if len(filteredExtensions) < 5 {
return nil, fmt.Errorf("got %d unused extensions, need at least 5", len(filteredExtensions))
}
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))
}
// shuffle extensions
for i := len(extensions) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
extensions[i], extensions[j] = extensions[j], extensions[i]
}
template.ImplantConfig.PollFileExt = extensions[0]
template.ImplantConfig.StagerFileExt = extensions[1]
template.ImplantConfig.StartSessionFileExt = extensions[2]
template.ImplantConfig.SessionFileExt = extensions[3]
template.ImplantConfig.CloseFileExt = extensions[4]
// randomly distribute the paths and filenames into the different segment types
template.ImplantConfig.CloseFiles = []string{}
template.ImplantConfig.SessionFiles = []string{}
template.ImplantConfig.PollFiles = []string{}
template.ImplantConfig.StagerFiles = []string{}
template.ImplantConfig.ClosePaths = []string{}
template.ImplantConfig.SessionPaths = []string{}
template.ImplantConfig.PollPaths = []string{}
template.ImplantConfig.StagerPaths = []string{}
for _, path := range paths {
switch rand.Intn(4) {
case 0:
template.ImplantConfig.PollPaths = append(template.ImplantConfig.PollPaths, path)
case 1:
template.ImplantConfig.SessionPaths = append(template.ImplantConfig.SessionPaths, path)
case 2:
template.ImplantConfig.ClosePaths = append(template.ImplantConfig.ClosePaths, path)
case 3:
template.ImplantConfig.StagerPaths = append(template.ImplantConfig.StagerPaths, path)
}
}
for _, filename := range filenames {
switch rand.Intn(4) {
case 0:
template.ImplantConfig.PollFiles = append(template.ImplantConfig.PollFiles, filename)
case 1:
template.ImplantConfig.SessionFiles = append(template.ImplantConfig.SessionFiles, filename)
case 2:
template.ImplantConfig.CloseFiles = append(template.ImplantConfig.CloseFiles, filename)
case 3:
template.ImplantConfig.StagerFiles = append(template.ImplantConfig.StagerFiles, filename)
}
}
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 (
"strings"
client "github.com/bishopfox/sliver/client/console"
"github.com/reeflective/console"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"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).
// cmd - The command to which the flags should be bound.
// 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.
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.
// 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)
bind(&comps)
carapace.Gen(cmd).FlagCompletion(comps)
}
// RestrictTargets generates a cobra annotation map with a single console.CommandHiddenFilter key
// to a comma-separated list of filters to use in order to expose/hide commands based on requirements.
// Ex: cmd.Annotations = RestrictTargets("windows") will only show the command if the target is Windows.
// Ex: cmd.Annotations = RestrictTargets("windows", "beacon") show the command if target is a beacon on Windows.
func RestrictTargets(filters ...string) map[string]string {
// hideCommand generates a cobra annotation map with a single
// console.CommandHiddenFilter key, which value is a comma-separated list
// of filters to use in order to expose/hide commands based on requirements.
// Ex: cmd.Annotations = hideCommand("windows") will hide the cmd
// if the target session/beacon is not a Windows host.
func hideCommand(filters ...string) map[string]string {
if len(filters) == 0 {
return nil
}
@@ -74,62 +76,3 @@ func RestrictTargets(filters ...string) map[string]string {
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(':')
})
}
// 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"
"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/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"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.
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackWordlistsCmd - Manage GPU cracking stations
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
if err != nil {
con.PrintErrorf("%s\n", err)
@@ -54,8 +55,8 @@ func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []str
}
}
// CrackRulesCmd - Manage GPU cracking stations.
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackRulesCmd - Manage GPU cracking stations
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
if err != nil {
con.PrintErrorf("%s\n", err)
@@ -74,8 +75,8 @@ func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
}
}
// CrackHcstat2Cmd - Manage GPU cracking stations.
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackHcstat2Cmd - Manage GPU cracking stations
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
if err != nil {
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.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{"Name", "Size"})
@@ -104,7 +105,7 @@ func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverClient)
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.SetTitle(console.Bold + "Wordlists" + console.Normal)
wordlistTable.SetStyle(settings.GetTableStyle(con))
@@ -161,8 +162,8 @@ func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverC
)
}
// CrackWordlistsAddCmd - Manage GPU cracking stations.
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackWordlistsAddCmd - Manage GPU cracking stations
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, _ := cmd.Flags().GetString("name")
var localPath string
@@ -205,8 +206,8 @@ func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []
addCrackFile(wordlist, crackFile, con)
}
// CrackRulesAddCmd - add a rules file.
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackRulesAddCmd - add a rules file
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, _ := cmd.Flags().GetString("name")
var localPath string
@@ -249,8 +250,8 @@ func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
addCrackFile(rules, crackFile, con)
}
// CrackHcstat2AddCmd - add a hcstat2 file.
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackHcstat2AddCmd - add a hcstat2 file
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name, _ := cmd.Flags().GetString("name")
var localPath string
@@ -293,7 +294,7 @@ func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []st
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()
wordlistReader := io.TeeReader(localFile, digest)
@@ -401,8 +402,8 @@ func readChunkAt(tmpFile *os.File, offset int64, chunkSize int64) ([]byte, error
return chunkBuf[:n], nil
}
// CrackWordlistsRmCmd - Manage GPU cracking stations.
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackWordlistsRmCmd - Manage GPU cracking stations
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var wordlistName string
if len(args) > 0 {
wordlistName = args[0]
@@ -435,8 +436,8 @@ func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []s
}
}
// CrackRulesRmCmd - Manage GPU cracking stations.
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackRulesRmCmd - Manage GPU cracking stations
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var rulesName string
if len(args) > 0 {
rulesName = args[0]
@@ -469,8 +470,8 @@ func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
}
}
// CrackHcstat2RmCmd - remove a hcstat2 file.
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CrackHcstat2RmCmd - remove a hcstat2 file
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var hcstat2Name string
if len(args) > 0 {
hcstat2Name = args[0]
+7 -7
View File
@@ -33,7 +33,7 @@ import (
)
// 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) {
PrintNoCrackstations(con)
} else {
@@ -58,7 +58,7 @@ func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
// 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{})
if err != nil {
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")
}
func AreCrackersOnline(con *console.SliverClient) bool {
func AreCrackersOnline(con *console.SliverConsoleClient) bool {
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
if err != nil {
return false
@@ -83,7 +83,7 @@ func AreCrackersOnline(con *console.SliverClient) bool {
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 {
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.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")
@@ -135,7 +135,7 @@ func printCracker(cracker *clientpb.Crackstation, index int, con *console.Sliver
printBenchmarks(cracker, con)
}
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverClient) {
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.SetTitle(console.Bold + "Benchmarks" + console.Normal)
+3 -3
View File
@@ -10,7 +10,7 @@ import (
"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 {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
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 {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
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 {
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
if err != nil {
+4 -4
View File
@@ -37,8 +37,8 @@ const (
CSVFormat = "csv" // username,hash\n
)
// CredsCmd - Add new credentials.
func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CredsCmd - Add new credentials
func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
collection, _ := cmd.Flags().GetString("collection")
username, _ := cmd.Flags().GetString("username")
plaintext, _ := cmd.Flags().GetString("plaintext")
@@ -76,8 +76,8 @@ func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
PrintCreds(creds.Credentials, con)
}
// CredsCmd - Add new credentials.
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CredsCmd - Add new credentials
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
collection, _ := cmd.Flags().GetString("collection")
filePath := args[0]
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"
"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/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
)
// CredsCmd - Manage credentials.
func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CredsCmd - Manage credentials
func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
@@ -46,7 +47,7 @@ func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
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)
for _, cred := range creds {
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.SetStyle(settings.GetTableStyle(con))
if collection != "" {
@@ -87,7 +88,7 @@ func printCollection(collection string, creds []*clientpb.Credential, con *conso
}
// 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 {
results := make([]string, 0)
@@ -100,8 +101,8 @@ func CredsHashTypeCompleter(con *console.SliverClient) carapace.Action {
})
}
// CredsHashFileFormatCompleter completes file formats for hash-files.
func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action {
// CredsHashFileFormatCompleter completes file formats for hash-files
func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionValuesDescribed(
UserColonHashNewlineFormat, "One hash per line.",
HashNewlineFormat, "A file containing lines of 'username:hash' pairs.",
@@ -109,8 +110,8 @@ func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action {
).Tag("hash file formats")
}
// CredsCollectionCompleter completes existing creds collection names.
func CredsCollectionCompleter(con *console.SliverClient) carapace.Action {
// CredsCollectionCompleter completes existing creds collection names
func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
results := make([]string, 0)
@@ -133,7 +134,7 @@ func CredsCollectionCompleter(con *console.SliverClient) carapace.Action {
}
// 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 {
results := make([]string, 0)
+2 -2
View File
@@ -27,8 +27,8 @@ import (
"github.com/spf13/cobra"
)
// CredsCmd - Add new credentials.
func CredsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CredsCmd - Add new credentials
func CredsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var id string
if len(args) > 0 {
id = args[0]
+3 -2
View File
@@ -9,13 +9,14 @@ import (
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// 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) {
// SelectCredential - Interactive menu for the user to select a credentials from the database
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverConsoleClient) (*clientpb.Credential, error) {
var creds *clientpb.Credentials
var err error
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"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord"
@@ -36,7 +38,6 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
)
var (
@@ -50,8 +51,8 @@ var (
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
)
// CursedChromeCmd - Execute a .NET assembly in-memory.
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CursedChromeCmd - Execute a .NET assembly in-memory
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
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)
if err != nil {
con.PrintErrorf("%s\n", err)
@@ -153,7 +154,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons
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"
if isEdge {
name = "Edge"
@@ -253,7 +254,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra
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")
if userDataFlag != "" {
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")
if exeFlag != "" {
return exeFlag, nil
@@ -416,7 +417,7 @@ func isChromeProcess(executable string) bool {
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{
Request: con.ActiveTarget.Request(cmd),
})
+6 -5
View File
@@ -27,14 +27,15 @@ import (
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/reeflective/readline"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"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)
if curse == nil {
return
@@ -55,7 +56,7 @@ func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
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 {
con.PrintErrorf("No debug targets\n")
return nil
@@ -93,7 +94,7 @@ var helperHooks = []string{
"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")
shell := readline.NewShell()
shell.History.AddFromFile("cursed history", tmpFile.Name())
+3 -2
View File
@@ -24,12 +24,13 @@ import (
"strings"
"time"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"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)
if curse == nil {
return
+7 -5
View File
@@ -21,20 +21,22 @@ package cursed
import (
"context"
"os"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
)
// CursedChromeCmd - Execute a .NET assembly in-memory.
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CursedChromeCmd - Execute a .NET assembly in-memory
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
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)
if err != nil {
con.PrintErrorf("%s\n", err)
@@ -151,7 +153,7 @@ func isEdgeProcess(executable string) bool {
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{
Request: con.ActiveTarget.Request(cmd),
})
+7 -6
View File
@@ -27,6 +27,8 @@ import (
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/overlord"
@@ -34,10 +36,9 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"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()
if session == nil {
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))
}
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)
if err != nil {
con.PrintErrorf("%s", err)
@@ -118,7 +119,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co
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{
Request: con.ActiveTarget.Request(cmd),
Path: electronExe,
@@ -129,7 +130,7 @@ func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra
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{
Request: con.ActiveTarget.Request(cmd),
})
@@ -147,7 +148,7 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *co
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))
debugPort := getRemoteDebuggerPort(cmd)
args := []string{
+3 -2
View File
@@ -23,13 +23,14 @@ import (
"strconv"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/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()
if session == nil {
return
+3 -2
View File
@@ -23,12 +23,13 @@ import (
"os"
"time"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"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)
if curse == nil {
return
+7 -6
View File
@@ -27,15 +27,16 @@ import (
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
// CursedChromeCmd - Execute a .NET assembly in-memory.
func CursedCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CursedChromeCmd - Execute a .NET assembly in-memory
func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
// Collect existing curses from core
cursedProcesses := [][]string{}
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.
func selectCursedProcess(con *console.SliverClient) *core.CursedProcess {
// selectCursedProcess - Interactively select a cursed process from a list
func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
cursedProcesses := []*core.CursedProcess{}
core.CursedProcesses.Range(func(key, value interface{}) bool {
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
// 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 (
localRefData []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
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()
if session == nil && beacon == nil {
return
@@ -66,7 +66,7 @@ func EnvGetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
// 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 != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
+2 -2
View File
@@ -32,7 +32,7 @@ import (
)
// 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()
if session == nil && beacon == nil {
return
@@ -72,7 +72,7 @@ func EnvSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
// 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 != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
+2 -2
View File
@@ -31,7 +31,7 @@ import (
)
// 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()
if session == nil && beacon == nil {
return
@@ -67,7 +67,7 @@ func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
// 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 != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
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"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory.
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -95,7 +96,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
Request: con.ActiveTarget.Request(cmd),
IsDLL: isDLL,
Process: process,
Arguments: assemblyArgs,
Arguments: assemblyArgsStr,
Assembly: assemblyBytes,
Arch: arch,
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")
lootName, _ := cmd.Flags().GetString("name")
+10 -8
View File
@@ -26,17 +26,19 @@ import (
"log"
"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/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"golang.org/x/term"
"google.golang.org/protobuf/proto"
)
// ExecuteShellcodeCmd - Execute shellcode in-memory.
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExecuteShellcodeCmd - Execute shellcode in-memory
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -126,8 +128,8 @@ func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []s
}
}
// PrintExecuteShellcode - Display result of shellcode execution.
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverClient) {
// PrintExecuteShellcode - Display result of shellcode execution
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
if task.Response.GetErr() != "" {
con.PrintErrorf("%s\n", task.Response.GetErr())
} 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
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
+14 -12
View File
@@ -23,16 +23,18 @@ import (
"strings"
"time"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// ExecuteCmd - Run a command on the remote system.
func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExecuteCmd - Run a command on the remote system
func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -64,7 +66,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
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)
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
stdout, _ := cmd.Flags().GetString("stdout")
saveLoot, _ := cmd.Flags().GetBool("loot")
@@ -134,8 +136,8 @@ func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName stri
PrintExecute(exec, cmd, con)
}
// PrintExecute - Print the output of an executed command.
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverClient) {
// PrintExecute - Print the output of an executed command
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverConsoleClient) {
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
stdout, _ := cmd.Flags().GetString("stdout")
stderr, _ := cmd.Flags().GetString("stderr")
@@ -208,7 +210,7 @@ func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStd
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 {
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
return
@@ -227,7 +229,7 @@ func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cm
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)
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 err error
@@ -256,7 +258,7 @@ func SaveExecutionOutput(executionOutput string, commandName string, hostName st
}
if outFilePath != nil {
outFilePath.WriteString(executionOutput)
outFilePath.Write([]byte(executionOutput))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
+38 -64
View File
@@ -21,19 +21,19 @@ package exec
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"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"
)
// MigrateCmd - Windows only, inject an implant into another process
func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSession()
if session == nil {
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")
return
}
var config *clientpb.ImplantConfig
var implantName string
if session != nil {
config = con.GetActiveSessionConfig()
implantName = session.Name
} else {
config = con.GetActiveBeaconConfig()
implantName = beacon.Name
if procName != "" {
procCtrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Searching for %s ...", procName), procCtrl)
proc, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(cmd),
})
if err != nil {
con.PrintErrorf("Error: %v\n", err)
return
}
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
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
encoder = clientpb.ShellcodeEncoder_NONE
}
ctrl := make(chan bool)
if pid != 0 {
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
}
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
Pid: pid,
Config: config,
Request: con.ActiveTarget.Request(cmd),
Encoder: encoder,
Name: implantName,
ProcName: procName,
Pid: pid,
Config: config,
Request: con.ActiveTarget.Request(cmd),
Encoder: encoder,
})
ctrl <- true
<-ctrl
@@ -91,32 +88,9 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintErrorf("Error: %v", err)
return
}
if migrate.Response != nil && migrate.Response.Async {
con.AddBeaconCallback(migrate.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, migrate)
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)
if !migrate.Success {
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
return
}
con.PrintInfof("Successfully migrated to %d\n", pid)
}
+9 -7
View File
@@ -22,16 +22,18 @@ import (
"context"
"fmt"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"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/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// MsfInjectCmd - Inject a metasploit payload into a remote process.
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -49,7 +51,7 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
return
}
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
}
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.
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverClient) {
// PrintMsfRemote - Print the results of the remote injection attempt
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
if msfRemote.Response == nil {
con.PrintErrorf("Empty response from msf payload injection task")
return
+6 -4
View File
@@ -22,15 +22,17 @@ import (
"context"
"fmt"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"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.
func MsfCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// MsfCmd - Inject a metasploit payload into the current remote process
func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
+5 -3
View File
@@ -21,11 +21,14 @@ package exec
import (
"context"
"fmt"
insecureRand "math/rand"
"os"
"strings"
"time"
insecureRand "math/rand"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
@@ -33,11 +36,10 @@ import (
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
)
// 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()
if session == nil {
return
+9 -15
View File
@@ -25,30 +25,24 @@ import (
"path/filepath"
"strings"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// SideloadCmd - Sideload a shared library on the remote system.
func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// SideloadCmd - Sideload a shared library on the remote system
func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
if len(args) < 1 {
cmd.Usage()
return
}
binPath := args[0]
var binArgs []string
if len(args) > 1 {
binArgs = args[1:]
}
binArgs := strings.Join(args[1:], " ")
entryPoint, _ := cmd.Flags().GetString("entry-point")
processName, _ := cmd.Flags().GetString("process")
@@ -65,7 +59,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
processArgs := strings.Split(processArgsStr, " ")
isDLL := (filepath.Ext(binPath) == ".dll")
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{
Request: con.ActiveTarget.Request(cmd),
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")
lootName, _ := cmd.Flags().GetString("name")
+9 -9
View File
@@ -22,25 +22,25 @@ import (
"context"
"fmt"
"os"
"strings"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// SpawnDllCmd - Spawn execution of a DLL on the remote system.
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// SpawnDllCmd - Spawn execution of a DLL on the remote system
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
binPath := args[0]
var dllArgs []string
if len(args) > 1 {
dllArgs = args[1:]
}
dllArgs := strings.Join(args[1:], " ")
processName, _ := cmd.Flags().GetString("process")
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")
lootName, _ := cmd.Flags().GetString("name")
+8 -7
View File
@@ -24,15 +24,16 @@ import (
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// 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) {
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
var (
privKey []byte
err error
@@ -119,8 +120,8 @@ func SSHCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
// PrintSSHCmd - Print the ssh command response.
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverClient) {
// PrintSSHCmd - Print the ssh command response
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
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 (
username string
password string
+2 -15
View File
@@ -25,13 +25,12 @@ import (
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/spf13/cobra"
)
// ExitCmd - Exit the console.
func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExitCmd - Exit the console
func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
fmt.Println("Exiting...")
if con.IsServer {
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)
}
// 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,
}}
}
-68
View File
@@ -1,68 +0,0 @@
package extensions
import (
"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 “ 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.ExecutionHelpGroup,
Run: func(cmd *cobra.Command, _ []string) {
ExtensionsCmd(cmd, con)
},
}
extensionCmd.AddCommand(&cobra.Command{
Use: consts.ListStr,
Short: "List extensions loaded in the current session or beacon",
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.ListStr}),
Run: func(cmd *cobra.Command, args []string) {
ExtensionsListCmd(cmd, con, args)
},
})
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}
}
+10 -9
View File
@@ -24,17 +24,18 @@ import (
"os"
"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/text"
"github.com/rsteube/carapace"
"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.
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverClient) {
// ExtensionsCmd - List information about installed extensions
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) {
if 0 < len(getInstalledManifests()) {
PrintExtensions(con)
} else {
@@ -42,8 +43,8 @@ func ExtensionsCmd(cmd *cobra.Command, con *console.SliverClient) {
}
}
// PrintExtensions - Print a list of loaded extensions.
func PrintExtensions(con *console.SliverClient) {
// PrintExtensions - Print a list of loaded extensions
func PrintExtensions(con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
@@ -115,8 +116,8 @@ func getInstalledManifests() map[string]*ExtensionManifest {
return installedManifests
}
// ExtensionsCommandNameCompleter - Completer for installed extensions command names.
func ExtensionsCommandNameCompleter(con *console.SliverClient) carapace.Action {
// ExtensionsCommandNameCompleter - Completer for installed extensions command names
func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
//installedManifests := getInstalledManifests()
results := []string{}
+15 -16
View File
@@ -25,14 +25,15 @@ import (
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
// ExtensionsInstallCmd - Install an extension.
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExtensionsInstallCmd - Install an extension
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
extLocalPath := args[0]
_, err := os.Stat(extLocalPath)
@@ -40,11 +41,11 @@ func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
return
}
InstallFromDir(extLocalPath, true, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
InstallFromDir(extLocalPath, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
}
// Install an extension from a directory
func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.SliverClient, isGz bool) {
func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz bool) {
var manifestData []byte
var err error
@@ -67,14 +68,12 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
//create repo path
minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestF.Name))
if _, err := os.Stat(minstallPath); !os.IsNotExist(err) {
if promptToOverwrite {
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
forceRemoveAll(minstallPath)
}
@@ -96,7 +95,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
for _, manifestFile := range manifest.Files {
if manifestFile.Path != "" {
if isGz {
err = installArtifact(extLocalPath, installPath, manifestFile.Path)
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
} else {
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
@@ -108,7 +107,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
}
err = util.CopyFile(src, dst)
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 {
@@ -121,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))
if err != nil {
return err
+4 -3
View File
@@ -21,13 +21,14 @@ package extensions
import (
"context"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
)
// ExtensionsListCmd - List all extension loaded on the active session/beacon.
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExtensionsListCmd - List all extension loaded on the active session/beacon
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
+72 -192
View File
@@ -21,8 +21,6 @@ package extensions
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -34,26 +32,26 @@ import (
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/packages"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
appConsole "github.com/reeflective/console"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"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 (
defaultTimeout = 60
// ManifestFileName - Extension manifest file name.
// ManifestFileName - Extension manifest file name
ManifestFileName = "extension.json"
)
@@ -87,21 +85,17 @@ type ExtensionManifest struct {
ExtCommand []*ExtCommand `json:"commands"`
RootPath string `json:"-"`
ArmoryName string `json:"-"`
ArmoryPK string `json:"-"`
RootPath string `json:"-"`
}
type ExtCommand struct {
CommandName string `json:"command_name"`
Help string `json:"help"`
LongHelp string `json:"long_help"`
Files []*extensionFile `json:"files"`
Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"depends_on"`
Init string `json:"init"`
Schema *packages.OutputSchema `json:"schema"`
CommandName string `json:"command_name"`
Help string `json:"help"`
LongHelp string `json:"long_help"`
Files []*extensionFile `json:"files"`
Arguments []*extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"depends_on"`
Manifest *ExtensionManifest
}
@@ -140,17 +134,17 @@ func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (strin
return filePath, nil
}
// ExtensionLoadCmd - Load extension command.
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExtensionLoadCmd - Load extension command
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
dirPath := args[0]
// dirPath := ctx.Args.String("dir-path")
manifest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
manyfest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
if err != nil {
return
}
// do not add if the command already exists
sliverMenu := con.App.Menu("implant")
for _, extCmd := range manifest.ExtCommand {
for _, extCmd := range manyfest.ExtCommand {
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
confirm := false
@@ -165,7 +159,7 @@ func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
}
}
// LoadExtensionManifest - Parse extension files.
// LoadExtensionManifest - Parse extension files
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
data, err := os.ReadFile(manifestPath)
if err != nil {
@@ -187,7 +181,7 @@ func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
func convertOldManifest(old *ExtensionManifest_) *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,
ExtensionAuthor: old.ExtensionAuthor,
OriginalAuthor: old.OriginalAuthor,
@@ -203,7 +197,6 @@ func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
Entrypoint: old.Entrypoint,
Files: old.Files,
Arguments: old.Arguments,
Schema: nil,
},
},
}
@@ -219,9 +212,7 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
err := json.Unmarshal(data, &extManifest)
if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible
//maybe it's an old manifest
if err != nil {
log.Printf("extension load error: %s", err)
}
log.Println(err)
oldmanifest := &ExtensionManifest_{}
err := json.Unmarshal(data, &oldmanifest)
if err != nil {
@@ -231,13 +222,9 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
//yes, ok, lets jigger it to a new manifest
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 {
command := extManifest.ExtCommand[i]
command.Manifest = extManifest
if command.Schema != nil {
command.Schema.IngestColumns()
}
extManifest.ExtCommand[i].Manifest = extManifest
}
return extManifest, validManifest(extManifest)
}
@@ -270,17 +257,12 @@ func validManifest(manifest *ExtensionManifest) error {
if extManifest.Help == "" {
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
}
// ExtensionRegisterCommand - Register a new extension command
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverClient) {
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverConsoleClient) {
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
con.PrintErrorf(errInvalidArgs.Error())
return
@@ -363,7 +345,7 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
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
binPath, err := ext.getFileForTarget(goos, goarch)
if err != nil {
@@ -384,50 +366,27 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
}
extensionList = extList.Names
}
//extensionList contains all *implant* loaded extensions. Is a sha256 sum of the relevant file.
//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[:])
depLoaded := false
for _, extName := range extensionList {
//check if extension we are trying to run (ext.CommandName or ext.DependsOn, for bofs) is already loaded
if extName == toberunhash {
//exists on the other side, exit early
if !depLoaded {
if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn {
depLoaded = true
}
}
if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName {
return nil
}
}
// 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 ext.DependsOn != "" {
return loadDep(goos, goarch, ext.DependsOn, cmd, con)
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// 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)
if err != nil {
@@ -439,45 +398,14 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
return nil
}
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) 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)
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverConsoleClient) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: name,
Data: binData,
OS: goos,
Name: ext.CommandName,
Data: binData,
OS: goos,
//Init: ext.Init, ?
Request: con.ActiveTarget.Request(cmd),
})
ctrl <- true
<-ctrl
if err != nil {
return err
}
@@ -487,7 +415,7 @@ func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.
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]
if ok {
depBinPath, err := depExt.getFileForTarget(goos, goarch)
@@ -503,7 +431,7 @@ func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con
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 (
err error
extensionArgs []byte
@@ -560,10 +488,16 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
} else {
// Regular DLL
extensionArgs, err = getExtArgs(cmd, args, binPath, ext)
if err != nil {
con.PrintErrorf("ext args error: %s\n", err)
return
// extArgs := strings.Join(args, " ")
//legacy case - single string arg
if len(ext.Arguments) == 1 && ext.Arguments[0].Type == "string" {
extensionArgs = []byte(strings.Join(args, " "))
} else {
extensionArgs, err = getExtArgs(cmd, args, binPath, ext)
if err != nil {
con.PrintErrorf("ext args error: %s\n", err)
return
}
}
extName = ext.CommandName
entryPoint = ext.Entrypoint
@@ -572,41 +506,8 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s ...", cmd.Name())
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{
Name: name,
Name: extName,
Export: entryPoint,
Args: extensionArgs,
Request: con.ActiveTarget.Request(cmd),
@@ -625,38 +526,23 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
con.PrintErrorf("Failed to decode call ext response %s\n", err)
return
}
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con)
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
})
con.PrintAsyncResponse(callExtResp.Response)
} else {
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con)
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
}
}
// PrintExtOutput - Print the ext execution output.
func PrintExtOutput(extName string, commandName string, outputSchema *packages.OutputSchema, callExtension *sliverpb.CallExtension, con *console.SliverClient) {
// PrintExtOutput - Print the ext execution output
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
if extName == commandName {
con.PrintInfof("Successfully executed %s\n", extName)
con.PrintInfof("Successfully executed %s", extName)
} 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 outputSchema == nil {
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)
}
}
con.PrintInfof("Got output:\n%s", callExtension.Output)
}
if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf(callExtension.Response.Err)
@@ -664,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
argsBuffer := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
@@ -673,12 +559,6 @@ func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]b
// Parse BOF arguments from grumble
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 {
// If we don't have any positional words left to consume,
// add the remaining required extension arguments in the
@@ -786,7 +666,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
return extensionArgs, nil
}
// CmdExists - checks if a command exists.
// CmdExists - checks if a command exists
func CmdExists(name string, cmd *cobra.Command) bool {
for _, c := range cmd.Commands() {
if name == c.Name() {
+7 -6
View File
@@ -25,14 +25,15 @@ import (
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
)
// ExtensionsRemoveCmd - Remove an extension.
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ExtensionsRemoveCmd - Remove an extension
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
name := args[0]
if name == "" {
con.PrintErrorf("Extension name is required\n")
@@ -63,8 +64,8 @@ func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []s
}
}
// RemoveExtensionByCommandName - Remove an extension by command name.
func RemoveExtensionByCommandName(commandName string, con *console.SliverClient) error {
// RemoveExtensionByCommandName - Remove an extension by command name
func RemoveExtensionByCommandName(commandName string, con *console.SliverConsoleClient) error {
if commandName == "" {
return errors.New("command name is required")
}
@@ -81,7 +82,7 @@ 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
func RemoveExtensionByManifestName(manifestName string, con *console.SliverClient) (bool, error) {
func RemoveExtensionByManifestName(manifestName string, con *console.SliverConsoleClient) (bool, error) {
if manifestName == "" {
return false, errors.New("command name is required")
}
+9 -12
View File
@@ -23,22 +23,22 @@ import (
"encoding/hex"
"fmt"
"os"
"strings"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"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/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// CatCmd - Display the contents of a remote file.
func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CatCmd - Display the contents of a remote file
func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -73,16 +73,16 @@ func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCat(filePath, download, cmd, con)
PrintCat(download, cmd, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(filePath, download, cmd, con)
PrintCat(download, cmd, con)
}
}
// PrintCat - Print the download to stdout.
func PrintCat(originalFileName string, download *sliverpb.Download, cmd *cobra.Command, con *console.SliverClient) {
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverConsoleClient) {
var (
lootDownload bool = true
err error
@@ -109,9 +109,6 @@ func PrintCat(originalFileName string, download *sliverpb.Download, cmd *cobra.C
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 err = colorize(download); err != nil {
con.Println(string(download.Data))
+6 -4
View File
@@ -21,15 +21,17 @@ package filesystem
import (
"context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// CdCmd - Change directory on the remote system.
func CdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// CdCmd - Change directory on the remote system
func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
+8 -6
View File
@@ -20,15 +20,17 @@ package filesystem
import (
"context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// ChmodCmd - Change the permissions of a file on the remote file system.
func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ChmodCmd - Change the permissions of a file on the remote file system
func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -75,8 +77,8 @@ func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
// PrintChmod - Print the chmod response.
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverClient) {
// PrintChmod - Print the chmod response
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverConsoleClient) {
if chmod.Response != nil && chmod.Response.Err != "" {
con.PrintErrorf("%s\n", chmod.Response.Err)
return
+8 -6
View File
@@ -20,15 +20,17 @@ package filesystem
import (
"context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// ChownCmd - Change the owner of a file on the remote file system.
func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// ChownCmd - Change the owner of a file on the remote file system
func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -83,8 +85,8 @@ func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
// PrintChown - Print the chown response.
func PrintChown(chown *sliverpb.Chown, con *console.SliverClient) {
// PrintChown - Print the chown response
func PrintChown(chown *sliverpb.Chown, con *console.SliverConsoleClient) {
if chown.Response != nil && chown.Response.Err != "" {
con.PrintErrorf("%s\n", chown.Response.Err)
return
+8 -6
View File
@@ -21,15 +21,17 @@ import (
"context"
"time"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// 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) {
// ChtimesCmd - Change the access and modified time of a file on the remote file system
func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -96,8 +98,8 @@ func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
// PrintChtimes - Print the Chtimes response.
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverClient) {
// PrintChtimes - Print the Chtimes response
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverConsoleClient) {
if chtimes.Response != nil && chtimes.Response.Err != "" {
con.PrintErrorf("%s\n", chtimes.Response.Err)
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 (
"context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"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()
if session == nil && beacon == nil {
return
@@ -70,7 +72,7 @@ func CpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err er
return
}
func PrintCp(cp *sliverpb.Cp, con *console.SliverClient) {
func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) {
if cp.Response != nil && cp.Response.Err != "" {
con.PrintErrorf("%s\n", cp.Response.Err)
return
+10 -15
View File
@@ -28,17 +28,18 @@ import (
"strings"
"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/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"gopkg.in/AlecAivazis/survey.v1"
)
func DownloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -97,12 +98,14 @@ func prettifyDownloadName(path string) string {
filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_")
// 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
}
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
if download.Response != nil && 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
// 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, "\\", "/")
}
remotePath := args[0]
var localPath string
if len(args) == 1 {
localPath = "."
+2 -2
View File
@@ -83,7 +83,7 @@ func processFlags(searchPattern string, insensitive bool, exact bool) string {
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()
if session == nil && beacon == nil {
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
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")
lootName, _ := cmd.Flags().GetString("name")
colorize, _ := cmd.Flags().GetBool("colorize-output")
+7 -4
View File
@@ -29,7 +29,7 @@ import (
"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()
if session == nil && beacon == nil {
return
@@ -63,7 +63,7 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
} else {
operationName = "bytes"
}
} else {
} else if cmd.Flags().Changed("lines") {
fetchBytes = false
fetchSize, _ = cmd.Flags().GetInt64("lines")
if fetchSize < 0 {
@@ -76,6 +76,9 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
} else {
operationName = "lines"
}
} else {
con.PrintErrorf("A number of bytes or a number of lines must be specified.")
return
}
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)
return
}
PrintCat(filePath, download, cmd, con)
PrintCat(download, cmd, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(filePath, download, cmd, con)
PrintCat(download, cmd, con)
}
}
+9 -7
View File
@@ -27,17 +27,19 @@ import (
"text/tabwriter"
"time"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"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.
func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// LsCmd - List the contents of a remote directory
func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -73,8 +75,8 @@ func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
// PrintLs - Display an sliverpb.Ls object.
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverClient) {
// PrintLs - Display an sliverpb.Ls object
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverConsoleClient) {
if ls.Response != nil && ls.Response.Err != "" {
con.PrintErrorf("%s\n", ls.Response.Err)
return
+7 -6
View File
@@ -20,15 +20,16 @@ package filesystem
import (
"context"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// MemfilesAddCmd - Add memfile.
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// MemfilesAddCmd - Add memfile
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -56,8 +57,8 @@ func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string
}
}
// PrintAddMemfile - Print the memfiles response.
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverClient) {
// PrintAddMemfile - Print the memfiles response
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverConsoleClient) {
if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" {
con.PrintErrorf("%s\n", memfilesAdd.Response.Err)
return
+7 -6
View File
@@ -25,16 +25,17 @@ import (
"text/tabwriter"
"time"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// MemfilesListCmd - List memfiles.
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// MemfilesListCmd - List memfiles
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -62,8 +63,8 @@ func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
}
}
// PrintMemfiles - Display an sliverpb.Ls object.
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverClient) {
// PrintMemfiles - Display an sliverpb.Ls object
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
if ls.Response != nil && ls.Response.Err != "" {
con.PrintErrorf("%s\n", ls.Response.Err)
return
+7 -6
View File
@@ -21,15 +21,16 @@ import (
"context"
"strconv"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// MemfilesRmCmd - Remove a memfile.
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// MemfilesRmCmd - Remove a memfile
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -69,8 +70,8 @@ func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
}
}
// PrintRmMemfile - Remove a memfile.
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverClient) {
// PrintRmMemfile - Remove a memfile
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverConsoleClient) {
if memfilesList.Response != nil && memfilesList.Response.Err != "" {
con.PrintErrorf("%s\n", memfilesList.Response.Err)
return
+8 -6
View File
@@ -21,15 +21,17 @@ package filesystem
import (
"context"
"google.golang.org/protobuf/proto"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
"google.golang.org/protobuf/proto"
)
// MkdirCmd - Make a remote directory.
func MkdirCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
// MkdirCmd - Make a remote directory
func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
@@ -66,8 +68,8 @@ func MkdirCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
}
}
// PrintMkdir - Print make directory.
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverClient) {
// PrintMkdir - Print make directory
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverConsoleClient) {
if mkdir.Response != nil && mkdir.Response.Err != "" {
con.PrintErrorf("%s\n", mkdir.Response.Err)
return
Loaded 100 of 3777 files, more files were not shown because too many files have changed in this diff. Show more