mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-09 01:49:40 -10:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f67e96e1de | ||
|
|
1301cf6027 | ||
|
|
ce72711bc3 | ||
|
|
b732ce1daa | ||
|
|
218e3c53b1 |
No files matched your search
@@ -31,7 +31,7 @@ jobs:
|
|||||||
run: make servers
|
run: make servers
|
||||||
|
|
||||||
- name: Server Artifacts
|
- name: Server Artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: servers
|
name: servers
|
||||||
path: ./sliver-server_*
|
path: ./sliver-server_*
|
||||||
@@ -42,10 +42,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
steps:
|
steps:
|
||||||
- name: Go 1.22
|
- name: Go 1.21
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "^1.22"
|
go-version: "^1.21"
|
||||||
|
|
||||||
- name: Check Out Code
|
- name: Check Out Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
run: make clients
|
run: make clients
|
||||||
|
|
||||||
- name: Client Artifacts
|
- name: Client Artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: clients
|
name: clients
|
||||||
path: ./sliver-client_*
|
path: ./sliver-client_*
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- run: mkdir -p ./builds
|
- run: mkdir -p ./builds
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: ./builds
|
path: ./builds
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Setup go environment
|
# Setup go environment
|
||||||
- name: Go 1.22
|
- name: Go 1.21
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "^1.22"
|
go-version: "^1.21"
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
config-file: ./.github/codeql/codeql-config.yml
|
config-file: ./.github/codeql/codeql-config.yml
|
||||||
languages: go
|
languages: go
|
||||||
@@ -48,6 +48,6 @@ jobs:
|
|||||||
run: make
|
run: make
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v2
|
||||||
env:
|
env:
|
||||||
VERSION: ${{github.sha}}
|
VERSION: ${{github.sha}}
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Detect package manager
|
- name: Detect package manager
|
||||||
id: detect-package-manager
|
id: detect-package-manager
|
||||||
@@ -63,10 +63,10 @@ jobs:
|
|||||||
node-version: "lts/*"
|
node-version: "lts/*"
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v5
|
uses: actions/configure-pages@v3
|
||||||
|
|
||||||
- name: Restore cache
|
- name: Restore cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.next/cache
|
.next/cache
|
||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
run: ${{ steps.detect-package-manager.outputs.runner }} next build
|
run: ${{ steps.detect-package-manager.outputs.runner }} next build
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v1
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/docs/sliver-docs/out
|
path: ${{ github.workspace }}/docs/sliver-docs/out
|
||||||
|
|
||||||
@@ -99,4 +99,4 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v2
|
||||||
@@ -23,15 +23,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Go 1.22
|
- name: Go 1.21
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "^1.22"
|
go-version: "^1.21"
|
||||||
|
|
||||||
- name: OS Packages
|
- name: OS Packages
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||||
|
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||||
|
|
||||||
- name: Check Out Code
|
- name: Check Out Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -56,10 +57,10 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Go 1.22
|
- name: Go 1.21
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "^1.22"
|
go-version: "^1.21"
|
||||||
|
|
||||||
- name: Mingw
|
- name: Mingw
|
||||||
run: brew install mingw-w64
|
run: brew install mingw-w64
|
||||||
@@ -74,6 +75,9 @@ jobs:
|
|||||||
run: make macos-amd64
|
run: make macos-amd64
|
||||||
|
|
||||||
- name: Test MacOS (amd64)
|
- name: Test MacOS (amd64)
|
||||||
|
env:
|
||||||
|
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
|
||||||
|
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
|
||||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||||
|
|
||||||
- name: Make MacOS (arm64)
|
- name: Make MacOS (arm64)
|
||||||
@@ -84,10 +88,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Go 1.22
|
- name: Go 1.21
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "^1.22"
|
go-version: "^1.21"
|
||||||
|
|
||||||
- name: Check Out Code
|
- name: Check Out Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
+12
-7
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
# STAGE: base
|
# STAGE: base
|
||||||
## Compiles Sliver for use
|
## Compiles Sliver for use
|
||||||
FROM golang:1.23.5 AS base
|
FROM --platform=linux/amd64 golang:1.21.4 as base
|
||||||
|
|
||||||
### Base packages
|
### Base packages
|
||||||
RUN apt-get update --fix-missing && apt-get -y install \
|
RUN apt-get update --fix-missing && apt-get -y install \
|
||||||
@@ -29,12 +29,12 @@ RUN cp -vv sliver-server /opt/sliver-server
|
|||||||
# STAGE: test
|
# STAGE: test
|
||||||
## Run unit tests against the compiled instance
|
## Run unit tests against the compiled instance
|
||||||
## Use `--target test` in the docker build command to run this stage
|
## Use `--target test` in the docker build command to run this stage
|
||||||
FROM base AS test
|
FROM --platform=linux/amd64 base as test
|
||||||
|
|
||||||
RUN apt-get update --fix-missing \
|
RUN apt-get update --fix-missing \
|
||||||
&& apt-get -y upgrade \
|
&& apt-get -y upgrade \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install \
|
||||||
curl
|
curl gcc-multilib build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||||
|
|
||||||
RUN /opt/sliver-server unpack --force
|
RUN /opt/sliver-server unpack --force
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
|
|||||||
|
|
||||||
# STAGE: production
|
# STAGE: production
|
||||||
## Final dockerized form of Sliver
|
## Final dockerized form of Sliver
|
||||||
FROM debian:bookworm-slim AS production
|
FROM --platform=linux/amd64 debian:bookworm-slim as production
|
||||||
|
|
||||||
### Install production packages
|
### Install production packages
|
||||||
RUN apt-get update --fix-missing \
|
RUN apt-get update --fix-missing \
|
||||||
@@ -56,7 +56,8 @@ RUN apt-get update --fix-missing \
|
|||||||
postgresql-contrib postgresql-client libpq-dev \
|
postgresql-contrib postgresql-client libpq-dev \
|
||||||
curl libapr1 libaprutil1 libsvn1 \
|
curl libapr1 libaprutil1 libsvn1 \
|
||||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||||
nasm
|
mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||||
|
nasm gcc-multilib
|
||||||
|
|
||||||
### Install MSF for stager generation
|
### Install MSF for stager generation
|
||||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
||||||
@@ -91,11 +92,15 @@ ENTRYPOINT [ "/opt/sliver-server" ]
|
|||||||
|
|
||||||
|
|
||||||
# STAGE: production-slim (about 1Gb smaller)
|
# STAGE: production-slim (about 1Gb smaller)
|
||||||
FROM debian:bookworm-slim AS production-slim
|
### Slim production image, i.e. without MSF and assoicated libraries
|
||||||
|
### Still include GCC and MinGW for cross-platform generation
|
||||||
|
FROM --platform=linux/amd64 debian:bookworm-slim as production-slim
|
||||||
|
|
||||||
### Install production packages
|
### Install production packages
|
||||||
RUN apt-get update --fix-missing \
|
RUN apt-get update --fix-missing \
|
||||||
&& apt-get -y upgrade
|
&& apt-get -y upgrade \
|
||||||
|
&& apt-get -y install \
|
||||||
|
build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||||
|
|
||||||
### Cleanup unneeded packages
|
### Cleanup unneeded packages
|
||||||
RUN apt-get autoremove -y \
|
RUN apt-get autoremove -y \
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
|
|||||||
|
|
||||||
# v1.6.0 / `master`
|
# v1.6.0 / `master`
|
||||||
|
|
||||||
**NOTE:** You are looking at the latest master branch of Sliver v1.6.0; new PRs should target this branch. However, this branch is NOT RECOMMENDED for production use yet. Please use release tagged versions for the best experience.
|
**NOTE:** You are looking the latest master branch of Sliver v1.6.0; new PRs should target this branch. However, this branch is NOT RECOMMENDED for production use yet. Please use release tagged versions for the best experience.
|
||||||
|
|
||||||
For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x/master` branch](https://github.com/BishopFox/sliver/tree/v1.5.x/master).
|
For PRs containing bug fixes specific to Sliver v1.5, please target the [`v1.5.x/master` branch](https://github.com/BishopFox/sliver/tree/v1.5.x/master).
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ See the [wiki](https://sliver.sh/docs?name=Compile+from+Source).
|
|||||||
|
|
||||||
### Feedback
|
### Feedback
|
||||||
|
|
||||||
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6).
|
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6)
|
||||||
|
|
||||||
### License - GPLv3
|
### License - GPLv3
|
||||||
|
|
||||||
|
|||||||
+11
-50
@@ -27,8 +27,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ArmoryConfigFileName = "armories.json"
|
armoryConfigFileName = "armories.json"
|
||||||
DefaultArmoryName = "Default"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -37,11 +36,9 @@ var (
|
|||||||
// DefaultArmoryRepoURL - The default repo url for the armory
|
// DefaultArmoryRepoURL - The default repo url for the armory
|
||||||
DefaultArmoryRepoURL string
|
DefaultArmoryRepoURL string
|
||||||
|
|
||||||
DefaultArmoryConfig = &ArmoryConfig{
|
defaultArmoryConfig = &ArmoryConfig{
|
||||||
PublicKey: DefaultArmoryPublicKey,
|
PublicKey: DefaultArmoryPublicKey,
|
||||||
RepoURL: DefaultArmoryRepoURL,
|
RepoURL: DefaultArmoryRepoURL,
|
||||||
Name: DefaultArmoryName,
|
|
||||||
Enabled: true,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,65 +48,29 @@ type ArmoryConfig struct {
|
|||||||
RepoURL string `json:"repo_url"`
|
RepoURL string `json:"repo_url"`
|
||||||
Authorization string `json:"authorization"`
|
Authorization string `json:"authorization"`
|
||||||
AuthorizationCmd string `json:"authorization_cmd"`
|
AuthorizationCmd string `json:"authorization_cmd"`
|
||||||
Name string `json:"name"`
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func RefreshArmoryAuthorization(armories []*ArmoryConfig) {
|
|
||||||
for _, armoryConfig := range armories {
|
|
||||||
if armoryConfig.AuthorizationCmd != "" {
|
|
||||||
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetArmoriesConfig - The parsed armory config file
|
// GetArmoriesConfig - The parsed armory config file
|
||||||
func GetArmoriesConfig() []*ArmoryConfig {
|
func GetArmoriesConfig() []*ArmoryConfig {
|
||||||
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName)
|
armoryConfigPath := filepath.Join(GetRootAppDir(), armoryConfigFileName)
|
||||||
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
|
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
|
||||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
return []*ArmoryConfig{defaultArmoryConfig}
|
||||||
}
|
}
|
||||||
data, err := os.ReadFile(armoryConfigPath)
|
data, err := os.ReadFile(armoryConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
return []*ArmoryConfig{defaultArmoryConfig}
|
||||||
}
|
}
|
||||||
var armoryConfigsFromFile []*ArmoryConfig
|
|
||||||
var armoryConfigs []*ArmoryConfig
|
var armoryConfigs []*ArmoryConfig
|
||||||
err = json.Unmarshal(data, &armoryConfigsFromFile)
|
err = json.Unmarshal(data, &armoryConfigs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
return []*ArmoryConfig{defaultArmoryConfig}
|
||||||
}
|
}
|
||||||
|
for _, armoryConfig := range armoryConfigs {
|
||||||
// Force the default armory to be the last
|
if armoryConfig.AuthorizationCmd != "" {
|
||||||
defaultArmorySpecified := false
|
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
|
||||||
|
|
||||||
for _, config := range armoryConfigsFromFile {
|
|
||||||
if config.Name == DefaultArmoryName {
|
|
||||||
defaultArmorySpecified = true
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
armoryConfigs = append(armoryConfigs, config)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if defaultArmorySpecified {
|
return append(armoryConfigs, defaultArmoryConfig)
|
||||||
armoryConfigs = append(armoryConfigs, DefaultArmoryConfig)
|
|
||||||
}
|
|
||||||
RefreshArmoryAuthorization(armoryConfigs)
|
|
||||||
|
|
||||||
return armoryConfigs
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveArmoriesConfig(armories []*ArmoryConfig) error {
|
|
||||||
configData, err := json.Marshal(armories)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName)
|
|
||||||
err = os.WriteFile(armoryConfigPath, configData, 0640)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
|
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
|
||||||
|
|||||||
+25
-12
@@ -35,7 +35,7 @@ type NameValueProbability struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
Probability int `json:"probability"`
|
Probability int `json:"probability"`
|
||||||
Method string `json:"method"`
|
Methods []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPC2ImplantConfig - Implant configuration options
|
// HTTPC2ImplantConfig - Implant configuration options
|
||||||
@@ -57,17 +57,30 @@ type HTTPC2ImplantConfig struct {
|
|||||||
NonceQueryArgChars string `json:"nonce_query_args"`
|
NonceQueryArgChars string `json:"nonce_query_args"`
|
||||||
URLParameters []NameValueProbability `json:"url_parameters"`
|
URLParameters []NameValueProbability `json:"url_parameters"`
|
||||||
Headers []NameValueProbability `json:"headers"`
|
Headers []NameValueProbability `json:"headers"`
|
||||||
NonceQueryLength int `json:"nonce_query_length"`
|
|
||||||
NonceMode string `json:"nonce_mode"`
|
|
||||||
|
|
||||||
MaxFileGen int `json:"max_files"`
|
MaxFiles int `json:"max_files"`
|
||||||
MinFileGen int `json:"min_files"`
|
MinFiles int `json:"min_files"`
|
||||||
MaxPathGen int `json:"max_paths"`
|
MaxPaths int `json:"max_paths"`
|
||||||
MinPathGen int `json:"min_paths"`
|
MinPaths int `json:"min_paths"`
|
||||||
MaxPathLength int `json:"max_path_length"`
|
|
||||||
MinPathLength int `json:"min_path_length"`
|
|
||||||
|
|
||||||
Extensions []string `json:"extensions"`
|
// Stager files and paths
|
||||||
Files []string `json:"files"`
|
StagerFileExt string `json:"stager_file_ext"`
|
||||||
Paths []string `json:"paths"`
|
StagerFiles []string `json:"stager_files"`
|
||||||
|
StagerPaths []string `json:"stager_paths"`
|
||||||
|
|
||||||
|
// Poll files and paths
|
||||||
|
PollFileExt string `json:"poll_file_ext"`
|
||||||
|
PollFiles []string `json:"poll_files"`
|
||||||
|
PollPaths []string `json:"poll_paths"`
|
||||||
|
|
||||||
|
// Session files and paths
|
||||||
|
StartSessionFileExt string `json:"start_session_file_ext"`
|
||||||
|
SessionFileExt string `json:"session_file_ext"`
|
||||||
|
SessionFiles []string `json:"session_files"`
|
||||||
|
SessionPaths []string `json:"session_paths"`
|
||||||
|
|
||||||
|
// Close session files and paths
|
||||||
|
CloseFileExt string `json:"close_file_ext"`
|
||||||
|
CloseFiles []string `json:"close_files"`
|
||||||
|
ClosePaths []string `json:"close_paths"`
|
||||||
}
|
}
|
||||||
+8
-9
@@ -24,17 +24,20 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
|
||||||
"github.com/rsteube/carapace"
|
"github.com/rsteube/carapace"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/client/console"
|
||||||
|
"github.com/bishopfox/sliver/client/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
logFileName = "sliver-client.log"
|
logFileName = "sliver-client.log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize logging.
|
var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||||
|
|
||||||
|
// Initialize logging
|
||||||
func initLogging(appDir string) *os.File {
|
func initLogging(appDir string) *os.File {
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
|
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
|
||||||
@@ -46,10 +49,6 @@ func initLogging(appDir string) *os.File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
appDir := assets.GetRootAppDir()
|
|
||||||
logFile := initLogging(appDir)
|
|
||||||
defer logFile.Close()
|
|
||||||
|
|
||||||
rootCmd.TraverseChildren = true
|
rootCmd.TraverseChildren = true
|
||||||
|
|
||||||
// Create the console client, without any RPC or commands bound to it yet.
|
// Create the console client, without any RPC or commands bound to it yet.
|
||||||
@@ -89,10 +88,10 @@ var rootCmd = &cobra.Command{
|
|||||||
Long: ``,
|
Long: ``,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute - Execute root command.
|
// Execute - Execute root command
|
||||||
func Execute() {
|
func Execute() {
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
fmt.Printf("root command: %s\n", err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
|
|
||||||
"gopkg.in/AlecAivazis/survey.v1"
|
"gopkg.in/AlecAivazis/survey.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+10
-42
@@ -1,53 +1,37 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
/*
|
|
||||||
Sliver Implant Framework
|
|
||||||
Copyright (C) 2019 Bishop Fox
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
"github.com/bishopfox/sliver/client/command"
|
"github.com/bishopfox/sliver/client/command"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/transport"
|
"github.com/bishopfox/sliver/client/transport"
|
||||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/connectivity"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// consoleCmd generates the console with required pre/post runners.
|
// consoleCmd generates the console with required pre/post runners
|
||||||
func consoleCmd(con *console.SliverClient) *cobra.Command {
|
func consoleCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||||
consoleCmd := &cobra.Command{
|
consoleCmd := &cobra.Command{
|
||||||
Use: "console",
|
Use: "console",
|
||||||
Short: "Start the sliver client console",
|
Short: "Start the sliver client console",
|
||||||
}
|
}
|
||||||
|
|
||||||
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
|
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
|
||||||
|
|
||||||
return consoleCmd
|
return consoleCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
|
func consoleRunnerCmd(con *console.SliverConsoleClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||||
var ln *grpc.ClientConn
|
var ln *grpc.ClientConn
|
||||||
|
|
||||||
pre = func(_ *cobra.Command, _ []string) error {
|
pre = func(_ *cobra.Command, _ []string) error {
|
||||||
|
appDir := assets.GetRootAppDir()
|
||||||
|
logFile := initLogging(appDir)
|
||||||
|
defer logFile.Close()
|
||||||
|
|
||||||
configs := assets.GetConfigs()
|
configs := assets.GetConfigs()
|
||||||
if len(configs) == 0 {
|
if len(configs) == 0 {
|
||||||
@@ -73,9 +57,6 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for any connection state changes and exit if the connection is lost.
|
|
||||||
go handleConnectionLost(ln)
|
|
||||||
|
|
||||||
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run)
|
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,16 +71,3 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
|
|||||||
|
|
||||||
return pre, post
|
return pre, post
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleConnectionLost(ln *grpc.ClientConn) {
|
|
||||||
currentState := ln.GetState()
|
|
||||||
// currentState should be "Ready" when the connection is established.
|
|
||||||
if ln.WaitForStateChange(context.Background(), currentState) {
|
|
||||||
newState := ln.GetState()
|
|
||||||
// newState will be "Idle" if the connection is lost.
|
|
||||||
if newState == connectivity.Idle {
|
|
||||||
fmt.Println("\nLost connection to server. Exiting now.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+9
-26
@@ -1,36 +1,19 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
/*
|
|
||||||
Sliver Implant Framework
|
|
||||||
Copyright (C) 2019 Bishop Fox
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/rsteube/carapace"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command"
|
"github.com/bishopfox/sliver/client/command"
|
||||||
"github.com/bishopfox/sliver/client/command/use"
|
"github.com/bishopfox/sliver/client/command/use"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/constants"
|
"github.com/bishopfox/sliver/client/constants"
|
||||||
"github.com/rsteube/carapace"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func implantCmd(con *console.SliverClient) *cobra.Command {
|
func implantCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||||
con.IsCLI = true
|
con.IsCLI = true
|
||||||
|
|
||||||
makeCommands := command.SliverCommands(con)
|
makeCommands := command.SliverCommands(con)
|
||||||
@@ -42,7 +25,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
|
|||||||
implantFlags.StringP("use", "s", "", "interact with a session")
|
implantFlags.StringP("use", "s", "", "interact with a session")
|
||||||
cmd.Flags().AddFlagSet(implantFlags)
|
cmd.Flags().AddFlagSet(implantFlags)
|
||||||
|
|
||||||
// Pre-runners (console setup, connection, etc)
|
// Prerunners (console setup, connection, etc)
|
||||||
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
|
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
|
||||||
|
|
||||||
// Completions
|
// Completions
|
||||||
@@ -51,7 +34,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRunners(implantCmd *cobra.Command, con *console.SliverClient) (pre, post func(cmd *cobra.Command, args []string) error) {
|
func makeRunners(implantCmd *cobra.Command, con *console.SliverConsoleClient) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||||
startConsole, closeConsole := consoleRunnerCmd(con, false)
|
startConsole, closeConsole := consoleRunnerCmd(con, false)
|
||||||
|
|
||||||
// The pre-run function connects to the server and sets up a "fake" console,
|
// The pre-run function connects to the server and sets up a "fake" console,
|
||||||
@@ -76,7 +59,7 @@ func makeRunners(implantCmd *cobra.Command, con *console.SliverClient) (pre, pos
|
|||||||
return pre, closeConsole
|
return pre, closeConsole
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCompleters(cmd *cobra.Command, con *console.SliverClient) {
|
func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
comps := carapace.Gen(cmd)
|
comps := carapace.Gen(cmd)
|
||||||
|
|
||||||
comps.PreRun(func(cmd *cobra.Command, args []string) {
|
comps.PreRun(func(cmd *cobra.Command, args []string) {
|
||||||
@@ -84,7 +67,7 @@ func makeCompleters(cmd *cobra.Command, con *console.SliverClient) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Bind completers to flags (wrap them to use the same pre-runners)
|
// Bind completers to flags (wrap them to use the same pre-runners)
|
||||||
command.BindFlagCompletions(cmd, func(comp *carapace.ActionMap) {
|
command.FlagComps(cmd, func(comp *carapace.ActionMap) {
|
||||||
(*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
(*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
cmd.PersistentPreRunE(cmd, c.Args)
|
cmd.PersistentPreRunE(cmd, c.Args)
|
||||||
return use.SessionIDCompleter(con)
|
return use.SessionIDCompleter(con)
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
|
||||||
"github.com/rsteube/carapace"
|
"github.com/rsteube/carapace"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func importCmd() *cobra.Command {
|
func importCmd() *cobra.Command {
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/version"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/client/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdVersion = &cobra.Command{
|
var cmdVersion = &cobra.Command{
|
||||||
|
|||||||
@@ -27,14 +27,15 @@ import (
|
|||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
|
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
"github.com/jedib0t/go-pretty/v6/text"
|
"github.com/jedib0t/go-pretty/v6/text"
|
||||||
"github.com/rsteube/carapace"
|
"github.com/rsteube/carapace"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AliasesCmd - The alias command.
|
// AliasesCmd - The alias command
|
||||||
func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) error {
|
func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) error {
|
||||||
if 0 < len(loadedAliases) {
|
if 0 < len(loadedAliases) {
|
||||||
PrintAliases(con)
|
PrintAliases(con)
|
||||||
} else {
|
} else {
|
||||||
@@ -44,8 +45,8 @@ func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintAliases - Print a list of loaded aliases.
|
// PrintAliases - Print a list of loaded aliases
|
||||||
func PrintAliases(con *console.SliverClient) {
|
func PrintAliases(con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
tw.AppendHeader(table.Row{
|
tw.AppendHeader(table.Row{
|
||||||
@@ -87,8 +88,8 @@ func PrintAliases(con *console.SliverClient) {
|
|||||||
con.Println(tw.Render())
|
con.Println(tw.Render())
|
||||||
}
|
}
|
||||||
|
|
||||||
// AliasCommandNameCompleter - Completer for installed extensions command names.
|
// AliasCommandNameCompleter - Completer for installed extensions command names
|
||||||
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverClient) []string {
|
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||||
results := []string{}
|
results := []string{}
|
||||||
for name := range loadedAliases {
|
for name := range loadedAliases {
|
||||||
if strings.HasPrefix(name, prefix) {
|
if strings.HasPrefix(name, prefix) {
|
||||||
@@ -128,7 +129,7 @@ func getInstalledManifests() map[string]*AliasManifest {
|
|||||||
return installedManifests
|
return installedManifests
|
||||||
}
|
}
|
||||||
|
|
||||||
// AliasCommandNameCompleter - Completer for installed extensions command names.
|
// AliasCommandNameCompleter - Completer for installed extensions command names
|
||||||
func AliasCompleter() carapace.Action {
|
func AliasCompleter() carapace.Action {
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
results := []string{}
|
results := []string{}
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -25,29 +25,30 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/util"
|
"github.com/bishopfox/sliver/util"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AliasesInstallCmd - Install an alias.
|
// AliasesInstallCmd - Install an alias
|
||||||
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
aliasLocalPath := args[0]
|
aliasLocalPath := args[0]
|
||||||
fi, err := os.Stat(aliasLocalPath)
|
fi, err := os.Stat(aliasLocalPath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
con.PrintErrorf("Alias path '%s' does not exist", aliasLocalPath)
|
con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !fi.IsDir() {
|
if !fi.IsDir() {
|
||||||
InstallFromFile(aliasLocalPath, "", false, con)
|
InstallFromFile(aliasLocalPath, false, con)
|
||||||
} else {
|
} else {
|
||||||
installFromDir(aliasLocalPath, con)
|
installFromDir(aliasLocalPath, con)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install an extension from a directory.
|
// Install an extension from a directory
|
||||||
func installFromDir(aliasLocalPath string, con *console.SliverClient) {
|
func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||||
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
|
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
|
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
|
||||||
@@ -99,8 +100,8 @@ func installFromDir(aliasLocalPath string, con *console.SliverClient) {
|
|||||||
con.Printf("done!\n")
|
con.Printf("done!\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install an extension from a .tar.gz file.
|
// Install an extension from a .tar.gz file
|
||||||
func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite bool, con *console.SliverClient) *string {
|
func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
|
||||||
manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName))
|
manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, aliasGzFilePath, err)
|
con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, aliasGzFilePath, err)
|
||||||
@@ -108,18 +109,12 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
|
|||||||
}
|
}
|
||||||
manifest, err := ParseAliasManifest(manifestData)
|
manifest, err := ParseAliasManifest(manifestData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorMsg := ""
|
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
|
||||||
if aliasName != "" {
|
|
||||||
errorMsg = fmt.Sprintf("Error processing manifest for alias %s - failed to parse %s: %s\n", aliasName, ManifestFileName, err)
|
|
||||||
} else {
|
|
||||||
errorMsg = fmt.Sprintf("Failed to parse %s: %s\n", ManifestFileName, err)
|
|
||||||
}
|
|
||||||
con.PrintErrorf(errorMsg)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
|
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
|
||||||
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
|
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
|
||||||
if promptToOverwrite {
|
if !autoOverwrite {
|
||||||
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
|
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
|
||||||
confirm := false
|
confirm := false
|
||||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||||
@@ -145,7 +140,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
|
|||||||
}
|
}
|
||||||
for _, aliasFile := range manifest.Files {
|
for _, aliasFile := range manifest.Files {
|
||||||
if aliasFile.Path != "" {
|
if aliasFile.Path != "" {
|
||||||
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path)
|
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("Failed to install file: %s\n", err)
|
con.PrintErrorf("Failed to install file: %s\n", err)
|
||||||
forceRemoveAll(installPath)
|
forceRemoveAll(installPath)
|
||||||
@@ -157,7 +152,7 @@ func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite
|
|||||||
return &installPath
|
return &installPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string) error {
|
func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error {
|
||||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator))))
|
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator))))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+54
-108
@@ -28,18 +28,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
|
||||||
"github.com/bishopfox/sliver/client/command/help"
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
|
||||||
consts "github.com/bishopfox/sliver/client/constants"
|
|
||||||
"github.com/bishopfox/sliver/client/packages"
|
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
|
||||||
"github.com/bishopfox/sliver/util"
|
|
||||||
app "github.com/reeflective/console"
|
app "github.com/reeflective/console"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
|
"github.com/bishopfox/sliver/client/command/help"
|
||||||
|
"github.com/bishopfox/sliver/client/console"
|
||||||
|
consts "github.com/bishopfox/sliver/client/constants"
|
||||||
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
|
"github.com/bishopfox/sliver/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -53,30 +53,30 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// alias name -> manifest/command.
|
// alias name -> manifest/command
|
||||||
loadedAliases = map[string]*loadedAlias{}
|
loadedAliases = map[string]*loadedAlias{}
|
||||||
|
|
||||||
defaultHostProc = map[string]string{
|
defaultHostProc = map[string]string{
|
||||||
"windows": windowsDefaultHostProc,
|
"windows": windowsDefaultHostProc,
|
||||||
"linux": linuxDefaultHostProc,
|
"linux": windowsDefaultHostProc,
|
||||||
"darwin": macosDefaultHostProc,
|
"darwin": macosDefaultHostProc,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ties the manifest struct to the command struct.
|
// Ties the manifest struct to the command struct
|
||||||
type loadedAlias struct {
|
type loadedAlias struct {
|
||||||
Manifest *AliasManifest
|
Manifest *AliasManifest
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// AliasFile - An OS/Arch specific file.
|
// AliasFile - An OS/Arch specific file
|
||||||
type AliasFile struct {
|
type AliasFile struct {
|
||||||
OS string `json:"os"`
|
OS string `json:"os"`
|
||||||
Arch string `json:"arch"`
|
Arch string `json:"arch"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AliasManifest - The manifest for an alias, contains metadata.
|
// AliasManifest - The manifest for an alias, contains metadata
|
||||||
type AliasManifest struct {
|
type AliasManifest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
@@ -86,17 +86,14 @@ type AliasManifest struct {
|
|||||||
Help string `json:"help"`
|
Help string `json:"help"`
|
||||||
LongHelp string `json:"long_help"`
|
LongHelp string `json:"long_help"`
|
||||||
|
|
||||||
Entrypoint string `json:"entrypoint"`
|
Entrypoint string `json:"entrypoint"`
|
||||||
AllowArgs bool `json:"allow_args"`
|
AllowArgs bool `json:"allow_args"`
|
||||||
DefaultArgs string `json:"default_args"`
|
DefaultArgs string `json:"default_args"`
|
||||||
Files []*AliasFile `json:"files"`
|
Files []*AliasFile `json:"files"`
|
||||||
IsReflective bool `json:"is_reflective"`
|
IsReflective bool `json:"is_reflective"`
|
||||||
IsAssembly bool `json:"is_assembly"`
|
IsAssembly bool `json:"is_assembly"`
|
||||||
Schema *packages.OutputSchema `json:"schema"`
|
|
||||||
|
|
||||||
RootPath string `json:"-"`
|
RootPath string `json:"-"`
|
||||||
ArmoryName string `json:"-"`
|
|
||||||
ArmoryPK string `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err error) {
|
func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err error) {
|
||||||
@@ -107,7 +104,7 @@ func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch string) (string, error) {
|
func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
|
||||||
filePath := ""
|
filePath := ""
|
||||||
for _, extFile := range a.Files {
|
for _, extFile := range a.Files {
|
||||||
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
||||||
@@ -127,7 +124,7 @@ func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AliasesLoadCmd - Locally load a alias into the Sliver shell.
|
// AliasesLoadCmd - Locally load a alias into the Sliver shell.
|
||||||
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
dirPath := args[0]
|
dirPath := args[0]
|
||||||
// dirPath := ctx.Args.String("dir-path")
|
// dirPath := ctx.Args.String("dir-path")
|
||||||
alias, err := LoadAlias(dirPath, cmd.Root(), con)
|
alias, err := LoadAlias(dirPath, cmd.Root(), con)
|
||||||
@@ -138,8 +135,8 @@ func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAlias - Load an alias into the Sliver shell from a given directory.
|
// LoadAlias - Load an alias into the Sliver shell from a given directory
|
||||||
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClient) (*AliasManifest, error) {
|
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||||
// retrieve alias manifest
|
// retrieve alias manifest
|
||||||
var err error
|
var err error
|
||||||
manifestPath, err = filepath.Abs(manifestPath)
|
manifestPath, err = filepath.Abs(manifestPath)
|
||||||
@@ -208,7 +205,7 @@ func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClien
|
|||||||
return aliasManifest, nil
|
return aliasManifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseAliasManifest - Parse an alias manifest.
|
// ParseAliasManifest - Parse an alias manifest
|
||||||
func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||||
// parse it
|
// parse it
|
||||||
alias := &AliasManifest{}
|
alias := &AliasManifest{}
|
||||||
@@ -241,17 +238,10 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if alias.Schema != nil {
|
|
||||||
if !packages.IsValidSchemaType(alias.Schema.Name) {
|
|
||||||
return nil, fmt.Errorf("%s is not a valid schema type", alias.Schema.Name)
|
|
||||||
}
|
|
||||||
alias.Schema.IngestColumns()
|
|
||||||
}
|
|
||||||
|
|
||||||
return alias, nil
|
return alias, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -278,20 +268,20 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// args := ctx.Args.StringList("arguments")
|
// args := ctx.Args.StringList("arguments")
|
||||||
var extArgsStr string
|
var extArgs string
|
||||||
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
|
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
|
||||||
extArgsStr = aliasManifest.DefaultArgs
|
extArgs = aliasManifest.DefaultArgs
|
||||||
} else {
|
} else {
|
||||||
extArgsStr = strings.Join(args, " ")
|
extArgs = strings.Join(args, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
extArgsStr = strings.TrimSpace(extArgsStr)
|
extArgs = strings.TrimSpace(extArgs)
|
||||||
entryPoint := aliasManifest.Entrypoint
|
entryPoint := aliasManifest.Entrypoint
|
||||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||||
// Special case for payloads with pass to Donut (.NET assemblies and sideloaded payloads):
|
// Special case for payloads with pass to Donut (.NET assemblies and sideloaded payloads):
|
||||||
// The Donut loader has a hard limit of 256 characters for the command line arguments, so
|
// The Donut loader has a hard limit of 256 characters for the command line arguments, so
|
||||||
// we're alerting the user that the arguments will be truncated.
|
// we're alerting the user that the arguments will be truncated.
|
||||||
if len(extArgsStr) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||||
msgStr := ""
|
msgStr := ""
|
||||||
// The --in-process flag only exists for .NET assemblies (aliasManifest.IsAssembly == true).
|
// The --in-process flag only exists for .NET assemblies (aliasManifest.IsAssembly == true).
|
||||||
// Groupping the two conditions together could crash the client since ctx.Flags.Type panics
|
// Groupping the two conditions together could crash the client since ctx.Flags.Type panics
|
||||||
@@ -363,13 +353,13 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
|
|
||||||
// Execute Assembly
|
// Execute Assembly
|
||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||||
con.SpinUntil(msg, ctrl)
|
con.SpinUntil(msg, ctrl)
|
||||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
IsDLL: isDLL,
|
IsDLL: isDLL,
|
||||||
Process: processName,
|
Process: processName,
|
||||||
Arguments: args,
|
Arguments: extArgs,
|
||||||
Assembly: binData,
|
Assembly: binData,
|
||||||
Arch: arch,
|
Arch: arch,
|
||||||
Method: method,
|
Method: method,
|
||||||
@@ -396,11 +386,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con)
|
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||||
})
|
})
|
||||||
con.PrintAsyncResponse(executeAssemblyResp.Response)
|
con.PrintAsyncResponse(executeAssemblyResp.Response)
|
||||||
} else {
|
} else {
|
||||||
PrintAssemblyOutput(cmd.Name(), aliasManifest.Schema, executeAssemblyResp, outFilePath, con)
|
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if aliasManifest.IsReflective {
|
} else if aliasManifest.IsReflective {
|
||||||
@@ -409,11 +399,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
|
|
||||||
// Spawn DLL
|
// Spawn DLL
|
||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||||
con.SpinUntil(msg, ctrl)
|
con.SpinUntil(msg, ctrl)
|
||||||
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
Args: args,
|
Args: strings.Trim(extArgs, " "),
|
||||||
Data: binData,
|
Data: binData,
|
||||||
ProcessName: processName,
|
ProcessName: processName,
|
||||||
EntryPoint: aliasManifest.Entrypoint,
|
EntryPoint: aliasManifest.Entrypoint,
|
||||||
@@ -435,11 +425,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con)
|
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||||
})
|
})
|
||||||
con.PrintAsyncResponse(spawnDllResp.Response)
|
con.PrintAsyncResponse(spawnDllResp.Response)
|
||||||
} else {
|
} else {
|
||||||
PrintSpawnDLLOutput(cmd.Name(), aliasManifest.Schema, spawnDllResp, outFilePath, con)
|
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -448,11 +438,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
|
|
||||||
// Sideload
|
// Sideload
|
||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||||
con.SpinUntil(msg, ctrl)
|
con.SpinUntil(msg, ctrl)
|
||||||
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
Args: args,
|
Args: extArgs,
|
||||||
Data: binData,
|
Data: binData,
|
||||||
EntryPoint: entryPoint,
|
EntryPoint: entryPoint,
|
||||||
ProcessName: processName,
|
ProcessName: processName,
|
||||||
@@ -475,80 +465,36 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con)
|
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||||
})
|
})
|
||||||
con.PrintAsyncResponse(sideloadResp.Response)
|
con.PrintAsyncResponse(sideloadResp.Response)
|
||||||
} else {
|
} else {
|
||||||
PrintSideloadOutput(cmd.Name(), aliasManifest.Schema, sideloadResp, outFilePath, con)
|
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOutputWithSchema(schema *packages.OutputSchema, result string) string {
|
// PrintSpawnDLLOutput - Prints the output of a spawn dll command
|
||||||
if schema == nil {
|
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||||
return result
|
con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
|
||||||
}
|
|
||||||
|
|
||||||
outputSchema := packages.GetNewPackageOutput(schema.Name)
|
|
||||||
if outputSchema == nil {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
err := outputSchema.IngestData([]byte(result), schema.Columns(), schema.GroupBy)
|
|
||||||
if err != nil {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
return outputSchema.CreateTable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintSpawnDLLOutput - Prints the output of a spawn dll command.
|
|
||||||
func PrintSpawnDLLOutput(cmdName string, schema *packages.OutputSchema, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverClient) {
|
|
||||||
var result string
|
|
||||||
|
|
||||||
if schema != nil {
|
|
||||||
result = getOutputWithSchema(schema, spawnDllResp.GetResult())
|
|
||||||
} else {
|
|
||||||
result = spawnDllResp.GetResult()
|
|
||||||
}
|
|
||||||
con.PrintInfof("%s output:\n%s", cmdName, result)
|
|
||||||
|
|
||||||
// Output the raw result to the file
|
|
||||||
if outFilePath != nil {
|
if outFilePath != nil {
|
||||||
outFilePath.WriteString(spawnDllResp.GetResult())
|
outFilePath.Write([]byte(spawnDllResp.GetResult()))
|
||||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintSideloadOutput - Prints the output of a sideload command.
|
// PrintSideloadOutput - Prints the output of a sideload command
|
||||||
func PrintSideloadOutput(cmdName string, schema *packages.OutputSchema, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverClient) {
|
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||||
var result string
|
con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
|
||||||
|
|
||||||
if schema != nil {
|
|
||||||
result = getOutputWithSchema(schema, sideloadResp.GetResult())
|
|
||||||
} else {
|
|
||||||
result = sideloadResp.GetResult()
|
|
||||||
}
|
|
||||||
con.PrintInfof("%s output:\n%s", cmdName, result)
|
|
||||||
|
|
||||||
// Output the raw result to the file
|
|
||||||
if outFilePath != nil {
|
if outFilePath != nil {
|
||||||
outFilePath.WriteString(sideloadResp.GetResult())
|
outFilePath.Write([]byte(sideloadResp.GetResult()))
|
||||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintAssemblyOutput - Prints the output of an execute-assembly command.
|
// PrintAssemblyOutput - Prints the output of an execute-assembly command
|
||||||
func PrintAssemblyOutput(cmdName string, schema *packages.OutputSchema, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverClient) {
|
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||||
var result string
|
con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
|
||||||
|
|
||||||
if schema != nil {
|
|
||||||
result = getOutputWithSchema(schema, string(execAsmResp.GetOutput()))
|
|
||||||
} else {
|
|
||||||
result = string(execAsmResp.GetOutput())
|
|
||||||
}
|
|
||||||
con.PrintInfof("%s output:\n%s", cmdName, result)
|
|
||||||
|
|
||||||
// Output the raw result to the file
|
|
||||||
if outFilePath != nil {
|
if outFilePath != nil {
|
||||||
outFilePath.Write(execAsmResp.GetOutput())
|
outFilePath.Write(execAsmResp.GetOutput())
|
||||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AliasesRemoveCmd - Locally load a alias into the Sliver shell.
|
// AliasesRemoveCmd - Locally load a alias into the Sliver shell.
|
||||||
func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
// name := ctx.Args.String("name")
|
// name := ctx.Args.String("name")
|
||||||
if name == "" {
|
if name == "" {
|
||||||
@@ -53,8 +54,8 @@ func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAliasByCommandName - Remove an alias by command name.
|
// RemoveAliasByCommandName - Remove an alias by command name
|
||||||
func RemoveAliasByCommandName(commandName string, con *console.SliverClient) error {
|
func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClient) error {
|
||||||
if commandName == "" {
|
if commandName == "" {
|
||||||
return errors.New("command name is required")
|
return errors.New("command name is required")
|
||||||
}
|
}
|
||||||
|
|||||||
+74
-364
@@ -23,7 +23,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -55,24 +54,13 @@ type ArmoryPackage struct {
|
|||||||
RepoURL string `json:"repo_url"`
|
RepoURL string `json:"repo_url"`
|
||||||
PublicKey string `json:"public_key"`
|
PublicKey string `json:"public_key"`
|
||||||
|
|
||||||
IsAlias bool `json:"-"`
|
IsAlias bool `json:"-"`
|
||||||
ArmoryName string `json:"-"`
|
|
||||||
/*
|
|
||||||
With support for multiple armories, the command name of a package
|
|
||||||
is not unique anymore, so we need something that is unique
|
|
||||||
to be able to keep track of packages.
|
|
||||||
|
|
||||||
This ID will be a hash calculated from properties of the package.
|
|
||||||
*/
|
|
||||||
ID string `json:"-"`
|
|
||||||
ArmoryPK string `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArmoryBundle - A list of packages
|
// ArmoryBundle - A list of packages
|
||||||
type ArmoryBundle struct {
|
type ArmoryBundle struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Packages []string `json:"packages"`
|
Packages []string `json:"packages"`
|
||||||
ArmoryName string `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArmoryHTTPConfig - Configuration for armory HTTP client
|
// ArmoryHTTPConfig - Configuration for armory HTTP client
|
||||||
@@ -101,75 +89,48 @@ type pkgCacheEntry struct {
|
|||||||
Alias *alias.AliasManifest
|
Alias *alias.AliasManifest
|
||||||
Extension *extensions.ExtensionManifest
|
Extension *extensions.ExtensionManifest
|
||||||
LastErr error
|
LastErr error
|
||||||
// This corresponds to Pkg.ID
|
|
||||||
ID string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// public key -> armoryCacheEntry
|
// public key -> armoryCacheEntry
|
||||||
indexCache = sync.Map{}
|
indexCache = sync.Map{}
|
||||||
// package ID -> armoryPkgCacheEntry
|
// public key -> armoryPkgCacheEntry
|
||||||
pkgCache = sync.Map{}
|
pkgCache = sync.Map{}
|
||||||
// public key -> assets.ArmoryConfig
|
|
||||||
currentArmories = sync.Map{}
|
|
||||||
|
|
||||||
// cacheTime - How long to cache the index/pkg manifests
|
// cacheTime - How long to cache the index/pkg manifests
|
||||||
//cacheTime = time.Hour
|
cacheTime = time.Hour
|
||||||
cacheTime = 2 * time.Minute
|
|
||||||
|
|
||||||
// This will kill a download if exceeded so needs to be large
|
// This will kill a download if exceeded so needs to be large
|
||||||
defaultTimeout = 15 * time.Minute
|
defaultTimeout = 15 * time.Minute
|
||||||
|
|
||||||
// Track whether armories have been initialized so that we know if we need to pull from the config
|
|
||||||
armoriesInitialized = false
|
|
||||||
|
|
||||||
// Track whether the default armory has been removed by the user (this is needed to prevent it from being readded in if they have removed it)
|
|
||||||
defaultArmoryRemoved = false
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArmoryCmd - The main armory command
|
// ArmoryCmd - The main armory command
|
||||||
func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
armoriesConfig := getCurrentArmoryConfiguration()
|
armoriesConfig := assets.GetArmoriesConfig()
|
||||||
if len(armoriesConfig) == 1 {
|
con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
|
||||||
con.Printf("Reading armory index ... ")
|
|
||||||
} else {
|
|
||||||
con.PrintInfof("Reading %d armory indexes ... ", len(armoriesConfig))
|
|
||||||
}
|
|
||||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||||
indexes := fetchIndexes(clientConfig)
|
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||||
if len(indexes) != len(armoriesConfig) {
|
if len(indexes) != len(armoriesConfig) {
|
||||||
|
con.Printf("errors!\n")
|
||||||
indexCache.Range(func(key, value interface{}) bool {
|
indexCache.Range(func(key, value interface{}) bool {
|
||||||
cacheEntry := value.(indexCacheEntry)
|
cacheEntry := value.(indexCacheEntry)
|
||||||
if cacheEntry.LastErr != nil {
|
if cacheEntry.LastErr != nil {
|
||||||
con.PrintErrorf("%s (%s) - %s\n", cacheEntry.ArmoryConfig.Name, cacheEntry.RepoURL, cacheEntry.LastErr)
|
con.PrintErrorf("%s - %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
con.Printf("done!\n")
|
con.Printf("done!\n")
|
||||||
}
|
}
|
||||||
armoriesInitialized = true
|
|
||||||
if len(indexes) == 0 {
|
|
||||||
con.PrintInfof("No indexes found\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
aliases := []*alias.AliasManifest{}
|
|
||||||
exts := []*extensions.ExtensionManifest{}
|
|
||||||
|
|
||||||
for _, index := range indexes {
|
if 0 < len(indexes) {
|
||||||
|
con.PrintInfof("Fetching package information ... ")
|
||||||
|
fetchPackageSignatures(indexes, clientConfig)
|
||||||
errorCount := 0
|
errorCount := 0
|
||||||
con.PrintInfof("Reading package information for armory %s ... ", index.ArmoryConfig.Name)
|
aliases := []*alias.AliasManifest{}
|
||||||
fetchPackageSignatures(index, clientConfig)
|
exts := []*extensions.ExtensionManifest{}
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
pkgCache.Range(func(key, value interface{}) bool {
|
||||||
cacheEntry, ok := value.(pkgCacheEntry)
|
cacheEntry := value.(pkgCacheEntry)
|
||||||
if !ok {
|
|
||||||
// Something is wrong with this entry
|
|
||||||
pkgCache.Delete(value)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if cacheEntry.ArmoryConfig.PublicKey != index.ArmoryConfig.PublicKey {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if cacheEntry.LastErr != nil {
|
if cacheEntry.LastErr != nil {
|
||||||
errorCount++
|
errorCount++
|
||||||
if errorCount == 0 {
|
if errorCount == 0 {
|
||||||
@@ -188,52 +149,32 @@ func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
if errorCount == 0 {
|
if errorCount == 0 {
|
||||||
con.Printf("done!\n")
|
con.Printf("done!\n")
|
||||||
}
|
}
|
||||||
}
|
if 0 < len(aliases) || 0 < len(exts) {
|
||||||
if 0 < len(aliases) || 0 < len(exts) {
|
con.Println()
|
||||||
|
PrintArmoryPackages(aliases, exts, con)
|
||||||
|
} else {
|
||||||
|
con.PrintInfof("No packages found\n")
|
||||||
|
}
|
||||||
|
|
||||||
con.Println()
|
con.Println()
|
||||||
PrintArmoryPackages(aliases, exts, con)
|
bundles := bundlesInCache()
|
||||||
|
if 0 < len(bundles) {
|
||||||
|
PrintArmoryBundles(bundles, con)
|
||||||
|
} else {
|
||||||
|
con.PrintInfof("No bundles found\n")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
con.PrintInfof("No packages found\n")
|
con.PrintInfof("No indexes found\n")
|
||||||
}
|
|
||||||
con.Println()
|
|
||||||
bundles := bundlesInCache()
|
|
||||||
if 0 < len(bundles) {
|
|
||||||
PrintArmoryBundles(bundles, con)
|
|
||||||
} else {
|
|
||||||
con.PrintInfof("No bundles found\n")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refresh(clientConfig ArmoryHTTPConfig) {
|
func refresh(clientConfig ArmoryHTTPConfig) {
|
||||||
getCurrentArmoryConfiguration()
|
armoriesConfig := assets.GetArmoriesConfig()
|
||||||
indexes := fetchIndexes(clientConfig)
|
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||||
for _, index := range indexes {
|
fetchPackageSignatures(indexes, clientConfig)
|
||||||
fetchPackageSignatures(index, clientConfig)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func countUniqueCommandsFromManifests(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest) (int, int) {
|
func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
|
||||||
uniqueAliasNames := []string{}
|
|
||||||
uniqueExtensionNames := []string{}
|
|
||||||
|
|
||||||
for _, alias := range aliases {
|
|
||||||
if !slices.Contains(uniqueAliasNames, alias.CommandName) {
|
|
||||||
uniqueAliasNames = append(uniqueAliasNames, alias.CommandName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ext := range exts {
|
|
||||||
for _, cmd := range ext.ExtCommand {
|
|
||||||
if !slices.Contains(uniqueExtensionNames, cmd.CommandName) {
|
|
||||||
uniqueExtensionNames = append(uniqueExtensionNames, cmd.CommandName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(uniqueAliasNames), len(uniqueExtensionNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
func packageManifestsInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
|
|
||||||
aliases := []*alias.AliasManifest{}
|
aliases := []*alias.AliasManifest{}
|
||||||
exts := []*extensions.ExtensionManifest{}
|
exts := []*extensions.ExtensionManifest{}
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
pkgCache.Range(func(key, value interface{}) bool {
|
||||||
@@ -250,125 +191,6 @@ func packageManifestsInCache() ([]*alias.AliasManifest, []*extensions.ExtensionM
|
|||||||
return aliases, exts
|
return aliases, exts
|
||||||
}
|
}
|
||||||
|
|
||||||
func armoryLookupByName(name string) *assets.ArmoryConfig {
|
|
||||||
var result *assets.ArmoryConfig
|
|
||||||
|
|
||||||
indexCache.Range(func(key, value interface{}) bool {
|
|
||||||
indexEntry, ok := value.(indexCacheEntry)
|
|
||||||
if !ok {
|
|
||||||
// Keep going
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if indexEntry.ArmoryConfig.Name == name {
|
|
||||||
result = indexEntry.ArmoryConfig
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the packages in the cache with a given name
|
|
||||||
func packageCacheLookupByName(name string) []*pkgCacheEntry {
|
|
||||||
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
|
|
||||||
|
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
|
||||||
cacheEntry, ok := value.(pkgCacheEntry)
|
|
||||||
if !ok {
|
|
||||||
// Keep going
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if cacheEntry.Pkg.Name == name {
|
|
||||||
result = append(result, &cacheEntry)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the packages in the cache for a given command name
|
|
||||||
func packageCacheLookupByCmd(commandName string) []*pkgCacheEntry {
|
|
||||||
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
|
|
||||||
|
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
|
||||||
cacheEntry, ok := value.(pkgCacheEntry)
|
|
||||||
if !ok {
|
|
||||||
// Keep going
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if cacheEntry.Pkg.CommandName == commandName {
|
|
||||||
result = append(result, &cacheEntry)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the package in the cache for a given command name and armory
|
|
||||||
func packageCacheLookupByCmdAndArmory(commandName string, armoryPublicKey string) *pkgCacheEntry {
|
|
||||||
var result *pkgCacheEntry
|
|
||||||
|
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
|
||||||
cacheEntry, ok := value.(pkgCacheEntry)
|
|
||||||
if !ok {
|
|
||||||
// Keep going
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey && cacheEntry.Pkg.CommandName == commandName {
|
|
||||||
result = &cacheEntry
|
|
||||||
// Stop iterating
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the package hashes in the cache for a given armory
|
|
||||||
func packageHashLookupByArmory(armoryPublicKey string) []string {
|
|
||||||
result := []string{}
|
|
||||||
|
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
|
||||||
cacheEntry, ok := value.(pkgCacheEntry)
|
|
||||||
if !ok {
|
|
||||||
// Keep going
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey {
|
|
||||||
result = append(result, cacheEntry.ID)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func packageCacheLookupByID(packageID string) *pkgCacheEntry {
|
|
||||||
var packageEntry *pkgCacheEntry
|
|
||||||
|
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
|
||||||
cacheEntry, ok := value.(pkgCacheEntry)
|
|
||||||
if !ok {
|
|
||||||
// Keep going
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if cacheEntry.LastErr == nil {
|
|
||||||
if cacheEntry.ID == packageID {
|
|
||||||
packageEntry = &cacheEntry
|
|
||||||
// Stop iterating
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return packageEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
func bundlesInCache() []*ArmoryBundle {
|
func bundlesInCache() []*ArmoryBundle {
|
||||||
bundles := []*ArmoryBundle{}
|
bundles := []*ArmoryBundle{}
|
||||||
indexCache.Range(func(key, value interface{}) bool {
|
indexCache.Range(func(key, value interface{}) bool {
|
||||||
@@ -385,7 +207,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
|
|||||||
var action carapace.Action
|
var action carapace.Action
|
||||||
|
|
||||||
results := []string{}
|
results := []string{}
|
||||||
aliases, exts := packageManifestsInCache()
|
aliases, exts := packagesInCache()
|
||||||
bundles := bundlesInCache()
|
bundles := bundlesInCache()
|
||||||
|
|
||||||
for _, aliasPkg := range aliases {
|
for _, aliasPkg := range aliases {
|
||||||
@@ -420,7 +242,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintArmoryPackages - Prints the armory packages
|
// PrintArmoryPackages - Prints the armory packages
|
||||||
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverClient) {
|
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) {
|
||||||
width, _, err := term.GetSize(0)
|
width, _, err := term.GetSize(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
width = 1
|
width = 1
|
||||||
@@ -434,7 +256,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
|
|
||||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||||
tw.AppendHeader(table.Row{
|
tw.AppendHeader(table.Row{
|
||||||
"Armory",
|
|
||||||
"Command Name",
|
"Command Name",
|
||||||
"Version",
|
"Version",
|
||||||
"Type",
|
"Type",
|
||||||
@@ -443,7 +264,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
tw.AppendHeader(table.Row{
|
tw.AppendHeader(table.Row{
|
||||||
"Armory",
|
|
||||||
"Command Name",
|
"Command Name",
|
||||||
"Version",
|
"Version",
|
||||||
"Type",
|
"Type",
|
||||||
@@ -453,11 +273,10 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
|
|
||||||
// Columns start at 1 for some dumb reason
|
// Columns start at 1 for some dumb reason
|
||||||
tw.SortBy([]table.SortBy{
|
tw.SortBy([]table.SortBy{
|
||||||
{Number: 2, Mode: table.Asc},
|
{Number: 1, Mode: table.Asc},
|
||||||
})
|
})
|
||||||
|
|
||||||
type pkgInfo struct {
|
type pkgInfo struct {
|
||||||
ArmoryName string
|
|
||||||
CommandName string
|
CommandName string
|
||||||
Version string
|
Version string
|
||||||
Type string
|
Type string
|
||||||
@@ -465,10 +284,8 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
URL string
|
URL string
|
||||||
}
|
}
|
||||||
entries := []pkgInfo{}
|
entries := []pkgInfo{}
|
||||||
|
|
||||||
for _, aliasPkg := range aliases {
|
for _, aliasPkg := range aliases {
|
||||||
entries = append(entries, pkgInfo{
|
entries = append(entries, pkgInfo{
|
||||||
ArmoryName: aliasPkg.ArmoryName,
|
|
||||||
CommandName: aliasPkg.CommandName,
|
CommandName: aliasPkg.CommandName,
|
||||||
Version: aliasPkg.Version,
|
Version: aliasPkg.Version,
|
||||||
Type: "Alias",
|
Type: "Alias",
|
||||||
@@ -479,7 +296,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
for _, extm := range exts {
|
for _, extm := range exts {
|
||||||
for _, extension := range extm.ExtCommand {
|
for _, extension := range extm.ExtCommand {
|
||||||
entries = append(entries, pkgInfo{
|
entries = append(entries, pkgInfo{
|
||||||
ArmoryName: extm.ArmoryName,
|
|
||||||
CommandName: extension.CommandName,
|
CommandName: extension.CommandName,
|
||||||
Version: extension.Manifest.Version,
|
Version: extension.Manifest.Version,
|
||||||
Type: "Extension",
|
Type: "Extension",
|
||||||
@@ -499,7 +315,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
}
|
}
|
||||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||||
rows = append(rows, table.Row{
|
rows = append(rows, table.Row{
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
|
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||||
@@ -508,7 +323,6 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
rows = append(rows, table.Row{
|
rows = append(rows, table.Row{
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
|
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||||
@@ -521,12 +335,11 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintArmoryBundles - Prints the armory bundles
|
// PrintArmoryBundles - Prints the armory bundles
|
||||||
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
|
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
tw.SetTitle(console.Bold + "Bundles" + console.Normal)
|
tw.SetTitle(console.Bold + "Bundles" + console.Normal)
|
||||||
tw.AppendHeader(table.Row{
|
tw.AppendHeader(table.Row{
|
||||||
"Armory Name",
|
|
||||||
"Name",
|
"Name",
|
||||||
"Contains",
|
"Contains",
|
||||||
})
|
})
|
||||||
@@ -552,7 +365,6 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
bundle.ArmoryName,
|
|
||||||
bundle.Name,
|
bundle.Name,
|
||||||
packages,
|
packages,
|
||||||
})
|
})
|
||||||
@@ -590,19 +402,12 @@ func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig {
|
|||||||
|
|
||||||
// fetch armory indexes, only returns indexes that were fetched successfully
|
// fetch armory indexes, only returns indexes that were fetched successfully
|
||||||
// errors are still in the cache objects however and can be checked
|
// errors are still in the cache objects however and can be checked
|
||||||
func fetchIndexes(clientConfig ArmoryHTTPConfig) []ArmoryIndex {
|
func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) []ArmoryIndex {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
// Try to get a max of 10 indexes at a time
|
for _, armoryConfig := range armoryConfigs {
|
||||||
currentRequests := make(chan struct{}, 10)
|
wg.Add(1)
|
||||||
currentArmories.Range(func(key, value interface{}) bool {
|
go fetchIndex(armoryConfig, clientConfig, wg)
|
||||||
armoryEntry := value.(assets.ArmoryConfig)
|
}
|
||||||
if armoryEntry.Enabled {
|
|
||||||
wg.Add(1)
|
|
||||||
currentRequests <- struct{}{}
|
|
||||||
go fetchIndex(&armoryEntry, currentRequests, clientConfig, wg)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
indexes := []ArmoryIndex{}
|
indexes := []ArmoryIndex{}
|
||||||
indexCache.Range(func(key, value interface{}) bool {
|
indexCache.Range(func(key, value interface{}) bool {
|
||||||
@@ -615,19 +420,13 @@ func fetchIndexes(clientConfig ArmoryHTTPConfig) []ArmoryIndex {
|
|||||||
return indexes
|
return indexes
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchIndex(armoryConfig *assets.ArmoryConfig, requestChannel chan struct{}, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
|
func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer func() {
|
|
||||||
<-requestChannel
|
|
||||||
}()
|
|
||||||
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
|
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
|
||||||
if ok {
|
if ok {
|
||||||
cached := cacheEntry.(indexCacheEntry)
|
cached := cacheEntry.(indexCacheEntry)
|
||||||
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
||||||
return
|
return
|
||||||
} else if time.Since(cached.Fetched) >= cacheTime {
|
|
||||||
// If an index has gone stale, remove it from the index cache
|
|
||||||
indexCache.Delete(armoryConfig.PublicKey)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,95 +463,40 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, requestChannel chan struct{},
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateHashesForIndex(index ArmoryIndex) []string {
|
func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig) {
|
||||||
result := []string{}
|
wg := &sync.WaitGroup{}
|
||||||
|
for _, index := range indexes {
|
||||||
for _, pkg := range index.Aliases {
|
for _, armoryPkg := range index.Extensions {
|
||||||
result = append(result, calculatePackageHash(pkg))
|
wg.Add(1)
|
||||||
}
|
armoryPkg.IsAlias = false
|
||||||
|
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
|
||||||
for _, pkg := range index.Extensions {
|
}
|
||||||
result = append(result, calculatePackageHash(pkg))
|
for _, armoryPkg := range index.Aliases {
|
||||||
}
|
wg.Add(1)
|
||||||
|
armoryPkg.IsAlias = true
|
||||||
return result
|
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
|
||||||
}
|
|
||||||
|
|
||||||
func makePackageCacheConsistent(index ArmoryIndex) {
|
|
||||||
packagesToRemove := []string{}
|
|
||||||
|
|
||||||
// Get the packages for the armory out of the cache
|
|
||||||
cacheHashesForArmory := packageHashLookupByArmory(index.ArmoryConfig.PublicKey)
|
|
||||||
indexHashesForArmory := calculateHashesForIndex(index)
|
|
||||||
|
|
||||||
if len(cacheHashesForArmory) > len(indexHashesForArmory) {
|
|
||||||
// Then there are packages in the cache that do not exist in the armory
|
|
||||||
if len(indexHashesForArmory) == 0 {
|
|
||||||
packagesToRemove = cacheHashesForArmory
|
|
||||||
} else {
|
|
||||||
for _, packageHash := range indexHashesForArmory {
|
|
||||||
if !slices.Contains(cacheHashesForArmory, packageHash) {
|
|
||||||
packagesToRemove = append(packagesToRemove, packageHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The remaining case of there being packages in the armory that do not exist in the cache
|
|
||||||
// will have to be solved with fetchPackageSignatures, and that function calls this one
|
|
||||||
// after fetching signatures and storing them in the cache, so that case should not apply
|
|
||||||
|
|
||||||
for _, packageHash := range packagesToRemove {
|
|
||||||
pkgCache.Delete(packageHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchPackageSignatures(index ArmoryIndex, clientConfig ArmoryHTTPConfig) {
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
// Be kind to armories and limit concurrent requests to 10
|
|
||||||
// This is an arbritrary number and we may have to tweak it if it causes problems
|
|
||||||
currentRequests := make(chan struct{}, 10)
|
|
||||||
for _, armoryPkg := range index.Extensions {
|
|
||||||
wg.Add(1)
|
|
||||||
currentRequests <- struct{}{}
|
|
||||||
armoryPkg.IsAlias = false
|
|
||||||
go fetchPackageSignature(wg, currentRequests, index.ArmoryConfig, armoryPkg, clientConfig)
|
|
||||||
}
|
|
||||||
for _, armoryPkg := range index.Aliases {
|
|
||||||
wg.Add(1)
|
|
||||||
currentRequests <- struct{}{}
|
|
||||||
armoryPkg.IsAlias = true
|
|
||||||
go fetchPackageSignature(wg, currentRequests, index.ArmoryConfig, armoryPkg, clientConfig)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
// If packages were deleted from the index, make sure the cache is consistent
|
|
||||||
makePackageCacheConsistent(index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchPackageSignature(wg *sync.WaitGroup, requestChannel chan struct{}, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
|
func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer func() {
|
cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
|
||||||
<-requestChannel
|
|
||||||
}()
|
|
||||||
cacheEntry, ok := pkgCache.Load(armoryPkg.ID)
|
|
||||||
if ok {
|
if ok {
|
||||||
cached := cacheEntry.(pkgCacheEntry)
|
cached := cacheEntry.(pkgCacheEntry)
|
||||||
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
||||||
return
|
return
|
||||||
} else if time.Since(cached.Fetched) >= cacheTime {
|
|
||||||
// If a package has gone stale, remove it from the package cache
|
|
||||||
pkgCache.Delete(armoryPkg.ID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgCacheEntry := &pkgCacheEntry{
|
pkgCacheEntry := &pkgCacheEntry{
|
||||||
ArmoryConfig: armoryConfig,
|
ArmoryConfig: armoryConfig,
|
||||||
RepoURL: armoryPkg.RepoURL,
|
RepoURL: armoryPkg.RepoURL,
|
||||||
ID: armoryPkg.ID,
|
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
pkgCacheEntry.Fetched = time.Now()
|
pkgCacheEntry.Fetched = time.Now()
|
||||||
pkgCache.Store(armoryPkg.ID, *pkgCacheEntry)
|
pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
repoURL, err := url.Parse(armoryPkg.RepoURL)
|
repoURL, err := url.Parse(armoryPkg.RepoURL)
|
||||||
@@ -784,53 +528,19 @@ func fetchPackageSignature(wg *sync.WaitGroup, requestChannel chan struct{}, arm
|
|||||||
if armoryPkg != nil {
|
if armoryPkg != nil {
|
||||||
pkgCacheEntry.Pkg = *armoryPkg
|
pkgCacheEntry.Pkg = *armoryPkg
|
||||||
}
|
}
|
||||||
|
if err == nil {
|
||||||
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
|
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
|
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
if armoryPkg.IsAlias {
|
|
||||||
pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
|
|
||||||
pkgCacheEntry.Alias.ArmoryName = armoryConfig.Name
|
|
||||||
pkgCacheEntry.Alias.ArmoryPK = armoryConfig.PublicKey
|
|
||||||
} else {
|
|
||||||
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
|
|
||||||
pkgCacheEntry.Extension.ArmoryName = armoryConfig.Name
|
|
||||||
pkgCacheEntry.Extension.ArmoryPK = armoryConfig.PublicKey
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func clearAllCaches() {
|
|
||||||
currentArmories.Range(func(key, value any) bool {
|
|
||||||
currentArmories.Delete(key)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
indexCache.Range(func(key, value any) bool {
|
|
||||||
indexCache.Delete(key)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
pkgCache.Range(func(key, value any) bool {
|
|
||||||
pkgCache.Delete(key)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func getArmoryPublicKey(armoryName string) string {
|
|
||||||
// Find PK for the armory name
|
|
||||||
armoryPK := ""
|
|
||||||
currentArmories.Range(func(key, value any) bool {
|
|
||||||
armoryEntry := value.(assets.ArmoryConfig)
|
|
||||||
if armoryEntry.Name == armoryName {
|
|
||||||
armoryPK = armoryEntry.PublicKey
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return true
|
if armoryPkg.IsAlias {
|
||||||
})
|
pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
|
||||||
|
} else {
|
||||||
return armoryPK
|
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -19,444 +19,122 @@ package armory
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
|
||||||
"github.com/bishopfox/sliver/client/command/alias"
|
"github.com/bishopfox/sliver/client/command/alias"
|
||||||
"github.com/bishopfox/sliver/client/command/extensions"
|
"github.com/bishopfox/sliver/client/command/extensions"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/constants"
|
"github.com/bishopfox/sliver/client/constants"
|
||||||
"github.com/bishopfox/sliver/util"
|
|
||||||
"github.com/bishopfox/sliver/util/minisign"
|
"github.com/bishopfox/sliver/util/minisign"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrPackageNotFound - The package was not found
|
// ErrPackageNotFound - The package was not found
|
||||||
var ErrPackageNotFound = errors.New("package not found")
|
var ErrPackageNotFound = errors.New("package not found")
|
||||||
var ErrPackageAlreadyInstalled = errors.New("package is already installed")
|
|
||||||
|
|
||||||
const (
|
|
||||||
doNotInstallOption = "Do not install this package"
|
|
||||||
doNotInstallPackageName = "do not install"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ArmoryInstallCmd - The armory install command
|
// ArmoryInstallCmd - The armory install command
|
||||||
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var promptToOverwrite bool
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
// name := ctx.Args.String("name")
|
||||||
if name == "" {
|
if name == "" {
|
||||||
con.PrintErrorf("A package or bundle name is required")
|
con.PrintErrorf("A package or bundle name is required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
forceInstallation, err := cmd.Flags().GetBool("force")
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Could not parse %q flag: %s\n", "force", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if forceInstallation {
|
|
||||||
promptToOverwrite = false
|
|
||||||
} else {
|
|
||||||
promptToOverwrite = true
|
|
||||||
}
|
|
||||||
|
|
||||||
armoryName, err := cmd.Flags().GetString("armory")
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find PK for the armory name
|
|
||||||
armoryPK := getArmoryPublicKey(armoryName)
|
|
||||||
|
|
||||||
// If the armory with the name is not found, print a warning
|
|
||||||
if cmd.Flags().Changed("armory") && armoryPK == "" {
|
|
||||||
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
|
|
||||||
}
|
|
||||||
|
|
||||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||||
refresh(clientConfig)
|
refresh(clientConfig)
|
||||||
if name == "all" {
|
if name == "all" {
|
||||||
aliases, extensions := packageManifestsInCache()
|
aliases, extensions := packagesInCache()
|
||||||
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
|
|
||||||
confirm := false
|
confirm := false
|
||||||
pluralAliases := "es"
|
|
||||||
if aliasCount == 1 {
|
|
||||||
pluralAliases = ""
|
|
||||||
}
|
|
||||||
pluralExtensions := "s"
|
|
||||||
if extCount == 1 {
|
|
||||||
pluralExtensions = ""
|
|
||||||
}
|
|
||||||
prompt := &survey.Confirm{
|
prompt := &survey.Confirm{
|
||||||
Message: fmt.Sprintf("Install %d alias%s and %d extension%s?",
|
Message: fmt.Sprintf("Install %d aliases and %d extensions?",
|
||||||
aliasCount, pluralAliases, extCount, pluralExtensions,
|
len(aliases), len(extensions),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
survey.AskOne(prompt, &confirm)
|
survey.AskOne(prompt, &confirm)
|
||||||
if !confirm {
|
if !confirm {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
promptToOverwrite = false
|
|
||||||
}
|
}
|
||||||
err = installPackageByName(name, armoryPK, forceInstallation, promptToOverwrite, clientConfig, con)
|
err := installPackageByName(name, clientConfig, con)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errors.Is(err, ErrPackageNotFound) {
|
if err == ErrPackageNotFound {
|
||||||
bundles := bundlesInCache()
|
bundles := bundlesInCache()
|
||||||
for _, bundle := range bundles {
|
for _, bundle := range bundles {
|
||||||
if bundle.Name == name {
|
if bundle.Name == name {
|
||||||
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con)
|
installBundle(bundle, clientConfig, con)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we have made it here, then there was not a bundle or package that matched the provided name
|
|
||||||
if armoryPK == "" {
|
|
||||||
con.PrintErrorf("No package or bundle named %q was found\n", name)
|
|
||||||
} else {
|
|
||||||
con.PrintErrorf("No package or bundle named %q was found in armory %s\n", name, armoryName)
|
|
||||||
}
|
|
||||||
} else if errors.Is(err, ErrPackageAlreadyInstalled) {
|
|
||||||
con.PrintErrorf("Package %q is already installed - use the force option to overwrite it\n", name)
|
|
||||||
} else {
|
|
||||||
con.PrintErrorf("Could not install package: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func installBundle(bundle *ArmoryBundle, armoryPK string, forceInstallation bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) {
|
|
||||||
installList := []string{}
|
|
||||||
pendingPackages := make(map[string]string)
|
|
||||||
|
|
||||||
for _, bundlePkgName := range bundle.Packages {
|
|
||||||
packageInstallList, err := buildInstallList(bundlePkgName, armoryPK, forceInstallation, pendingPackages)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, ErrPackageAlreadyInstalled) {
|
|
||||||
con.PrintInfof("Package %s is already installed. Skipping...\n", bundlePkgName)
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
con.PrintErrorf("Error for package %s: %s\n", bundlePkgName, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, pkgID := range packageInstallList {
|
|
||||||
if !slices.Contains(installList, pkgID) {
|
|
||||||
installList = append(installList, pkgID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, packageID := range installList {
|
|
||||||
packageEntry := packageCacheLookupByID(packageID)
|
|
||||||
if packageEntry == nil {
|
|
||||||
con.PrintErrorf("The package cache is out of date. Please run armory refresh and try again.\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if packageEntry.Pkg.IsAlias {
|
|
||||||
err := installAliasPackage(packageEntry, false, clientConfig, con)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Failed to install alias '%s': %s", packageEntry.Alias.CommandName, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := installExtensionPackage(packageEntry, false, clientConfig, con)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Failed to install extension '%s': %s", packageEntry.Extension.Name, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
con.PrintErrorf("No package or bundle named '%s' was found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInstalledPackageNames() []string {
|
func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||||
packageNames := []string{}
|
for _, pkgName := range bundle.Packages {
|
||||||
|
err := installPackageByName(pkgName, clientConfig, con)
|
||||||
installedAliases := assets.GetInstalledAliasManifests()
|
|
||||||
installedExtensions := assets.GetInstalledExtensionManifests()
|
|
||||||
|
|
||||||
for _, aliasFileName := range installedAliases {
|
|
||||||
alias := &alias.AliasManifest{}
|
|
||||||
manifestData, err := os.ReadFile(aliasFileName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
con.PrintErrorf("Failed to install '%s': %s", pkgName, err)
|
||||||
}
|
|
||||||
err = json.Unmarshal(manifestData, alias)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !slices.Contains(packageNames, alias.CommandName) {
|
|
||||||
packageNames = append(packageNames, alias.CommandName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, extensionFileName := range installedExtensions {
|
|
||||||
extension := &extensions.ExtensionManifest{}
|
|
||||||
manifestData, err := os.ReadFile(extensionFileName)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(manifestData, extension)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(extension.ExtCommand) == 0 {
|
|
||||||
extensionOld := &extensions.ExtensionManifest_{}
|
|
||||||
// Some extension manifests are using an older version
|
|
||||||
// To maintain compatibility with those extensions, we will
|
|
||||||
// re-unmarshal the data as the older version
|
|
||||||
err = json.Unmarshal(manifestData, extensionOld)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !slices.Contains(packageNames, extensionOld.CommandName) {
|
|
||||||
packageNames = append(packageNames, extensionOld.CommandName)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, command := range extension.ExtCommand {
|
|
||||||
if !slices.Contains(packageNames, command.CommandName) {
|
|
||||||
packageNames = append(packageNames, command.CommandName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return packageNames
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a convenience function to get the names of the commands in the cache
|
func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
|
||||||
func getCommandsInCache() []string {
|
aliases, extensions := packagesInCache()
|
||||||
commandNames := []string{}
|
for _, alias := range aliases {
|
||||||
|
if alias.CommandName == name || name == "all" {
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
installAlias(alias, clientConfig, con)
|
||||||
cacheEntry := value.(pkgCacheEntry)
|
if name != "all" {
|
||||||
if cacheEntry.LastErr == nil {
|
return nil
|
||||||
if cacheEntry.Pkg.IsAlias {
|
|
||||||
if !slices.Contains(commandNames, cacheEntry.Alias.CommandName) {
|
|
||||||
commandNames = append(commandNames, cacheEntry.Alias.CommandName)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, command := range cacheEntry.Extension.ExtCommand {
|
|
||||||
if !slices.Contains(commandNames, command.CommandName) {
|
|
||||||
commandNames = append(commandNames, command.CommandName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return commandNames
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPackagesWithCommandName(name, armoryPK, minimumVersion string) []*pkgCacheEntry {
|
|
||||||
packages := []*pkgCacheEntry{}
|
|
||||||
|
|
||||||
pkgCache.Range(func(key, value interface{}) bool {
|
|
||||||
cacheEntry := value.(pkgCacheEntry)
|
|
||||||
if cacheEntry.LastErr == nil {
|
|
||||||
if cacheEntry.Pkg.IsAlias {
|
|
||||||
if cacheEntry.Alias.CommandName == name {
|
|
||||||
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Alias.Version >= minimumVersion) {
|
|
||||||
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
|
|
||||||
packages = append(packages, &cacheEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, command := range cacheEntry.Extension.ExtCommand {
|
|
||||||
if command.CommandName == name {
|
|
||||||
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Extension.Version >= minimumVersion) {
|
|
||||||
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
|
|
||||||
packages = append(packages, &cacheEntry)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return packages
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPackageIDFromUser(name string, options map[string]string) string {
|
|
||||||
selectedPackageKey := ""
|
|
||||||
optionKeys := util.Keys(options)
|
|
||||||
slices.Sort(optionKeys)
|
|
||||||
// Add a cancel option
|
|
||||||
optionKeys = append(optionKeys, doNotInstallOption)
|
|
||||||
options[doNotInstallOption] = doNotInstallPackageName
|
|
||||||
prompt := &survey.Select{
|
|
||||||
Message: fmt.Sprintf("More than one package contains the command %s. Please choose an option from the list below:", name),
|
|
||||||
Options: optionKeys,
|
|
||||||
}
|
|
||||||
survey.AskOne(prompt, &selectedPackageKey)
|
|
||||||
selectedPackageID := options[selectedPackageKey]
|
|
||||||
|
|
||||||
return selectedPackageID
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPackageForCommand(name, armoryPK, minimumVersion string) (*pkgCacheEntry, error) {
|
|
||||||
packagesWithCommand := getPackagesWithCommandName(name, armoryPK, minimumVersion)
|
|
||||||
|
|
||||||
if len(packagesWithCommand) > 1 {
|
|
||||||
// Build an option map for the user to choose from (option -> pkgID)
|
|
||||||
optionMap := make(map[string]string)
|
|
||||||
for _, packageEntry := range packagesWithCommand {
|
|
||||||
var optionName string
|
|
||||||
if packageEntry.Pkg.IsAlias {
|
|
||||||
optionName = fmt.Sprintf("Alias %s %s from armory %s (%s)",
|
|
||||||
name,
|
|
||||||
packageEntry.Alias.Version,
|
|
||||||
packageEntry.ArmoryConfig.Name,
|
|
||||||
packageEntry.Pkg.RepoURL,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
optionName = fmt.Sprintf("Extension %s %s from armory %s with command %s (%s)",
|
|
||||||
packageEntry.Pkg.Name,
|
|
||||||
packageEntry.Extension.Version,
|
|
||||||
packageEntry.ArmoryConfig.Name,
|
|
||||||
name,
|
|
||||||
packageEntry.Pkg.RepoURL,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
optionMap[optionName] = packageEntry.ID
|
|
||||||
}
|
|
||||||
selectedPackageID := getPackageIDFromUser(name, optionMap)
|
|
||||||
if selectedPackageID == doNotInstallPackageName {
|
|
||||||
return nil, fmt.Errorf("user cancelled installation")
|
|
||||||
}
|
|
||||||
for _, packageEntry := range packagesWithCommand {
|
|
||||||
if packageEntry.ID == selectedPackageID {
|
|
||||||
return packageEntry, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if len(packagesWithCommand) == 1 {
|
|
||||||
return packagesWithCommand[0], nil
|
|
||||||
}
|
|
||||||
return nil, ErrPackageNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildInstallList(name, armoryPK string, forceInstallation bool, pendingPackages map[string]string) ([]string, error) {
|
|
||||||
packageInstallList := []string{}
|
|
||||||
installedPackages := getInstalledPackageNames()
|
|
||||||
|
|
||||||
/*
|
|
||||||
Gather information about what we are working with
|
|
||||||
|
|
||||||
Find all conflicts within aliases for a given name (or all names), same thing with extensions
|
|
||||||
Then if there are aliases and extensions with a given name, make sure to note that for when we ask the user what to do
|
|
||||||
*/
|
|
||||||
var requestedPackageList []string
|
|
||||||
if name == "all" {
|
|
||||||
requestedPackageList = []string{}
|
|
||||||
allCommands := getCommandsInCache()
|
|
||||||
for _, cmdName := range allCommands {
|
|
||||||
if !slices.Contains(installedPackages, cmdName) || forceInstallation {
|
|
||||||
// Check to see if there is a package pending with that name
|
|
||||||
if _, ok := pendingPackages[cmdName]; !ok {
|
|
||||||
requestedPackageList = append(requestedPackageList, cmdName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !slices.Contains(installedPackages, name) || forceInstallation {
|
|
||||||
// Check to see if there is a package pending with that name
|
|
||||||
if _, ok := pendingPackages[name]; !ok {
|
|
||||||
requestedPackageList = []string{name}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, ErrPackageAlreadyInstalled
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
for _, extm := range extensions {
|
||||||
for _, packageName := range requestedPackageList {
|
for _, ext := range extm.ExtCommand {
|
||||||
if _, ok := pendingPackages[packageName]; ok {
|
if ext.CommandName == name || name == "all" {
|
||||||
// We are already going to install a package with this name, so do not try to resolve it
|
installExtension(ext.Manifest, clientConfig, con)
|
||||||
continue
|
if name != "all" {
|
||||||
}
|
return nil
|
||||||
packageEntry, err := getPackageForCommand(packageName, armoryPK, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !slices.Contains(packageInstallList, packageEntry.ID) {
|
|
||||||
packageInstallList = append(packageInstallList, packageEntry.ID)
|
|
||||||
pendingPackages[packageName] = packageEntry.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
if !packageEntry.Pkg.IsAlias {
|
|
||||||
dependencies := make(map[string]*pkgCacheEntry)
|
|
||||||
err = resolveExtensionPackageDependencies(packageEntry, dependencies, pendingPackages)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for pkgName, packageEntry := range dependencies {
|
|
||||||
if !slices.Contains(packageInstallList, packageEntry.ID) {
|
|
||||||
packageInstallList = append(packageInstallList, packageEntry.ID)
|
|
||||||
}
|
|
||||||
if _, ok := pendingPackages[pkgName]; !ok {
|
|
||||||
pendingPackages[pkgName] = packageEntry.ID
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return packageInstallList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func installPackageByName(name, armoryPK string, forceInstallation, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
|
||||||
pendingPackages := make(map[string]string)
|
|
||||||
packageInstallList, err := buildInstallList(name, armoryPK, forceInstallation, pendingPackages)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(packageInstallList) > 0 {
|
|
||||||
for _, packageID := range packageInstallList {
|
|
||||||
entry := packageCacheLookupByID(packageID)
|
|
||||||
if entry == nil {
|
|
||||||
return errors.New("cache consistency error - please refresh the cache and try again")
|
|
||||||
}
|
|
||||||
if entry.Pkg.IsAlias {
|
|
||||||
err := installAliasPackage(entry, promptToOverwrite, clientConfig, con)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to install alias '%s': %s", entry.Alias.CommandName, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := installExtensionPackage(entry, promptToOverwrite, clientConfig, con)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to install extension '%s': %s", entry.Extension.Name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ErrPackageNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == "all" {
|
if name == "all" {
|
||||||
con.Printf("\n")
|
con.Printf("\n")
|
||||||
con.PrintInfof("Operation complete\n")
|
con.PrintInfof("All packages installed\n")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
return ErrPackageNotFound
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
func installAlias(alias *alias.AliasManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||||
|
err := installAliasPackageByName(alias.CommandName, clientConfig, con)
|
||||||
|
if err != nil {
|
||||||
|
con.PrintErrorf("Failed to install alias '%s': %s", alias.CommandName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
|
||||||
|
var entry *pkgCacheEntry
|
||||||
|
pkgCache.Range(func(key, value interface{}) bool {
|
||||||
|
cacheEntry := value.(pkgCacheEntry)
|
||||||
|
if cacheEntry.Pkg.CommandName == name {
|
||||||
|
entry = &cacheEntry
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
return errors.New("package not found")
|
return errors.New("package not found")
|
||||||
}
|
}
|
||||||
if !entry.Pkg.IsAlias {
|
|
||||||
return errors.New("package is not an alias")
|
|
||||||
}
|
|
||||||
repoURL, err := url.Parse(entry.RepoURL)
|
repoURL, err := url.Parse(entry.RepoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -496,7 +174,7 @@ func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientCon
|
|||||||
|
|
||||||
con.Printf(console.Clearln + "\r") // Clear the line
|
con.Printf(console.Clearln + "\r") // Clear the line
|
||||||
|
|
||||||
installPath := alias.InstallFromFile(tmpFile.Name(), entry.Alias.CommandName, promptToOverwrite, con)
|
installPath := alias.InstallFromFile(tmpFile.Name(), true, con)
|
||||||
if installPath == nil {
|
if installPath == nil {
|
||||||
return errors.New("failed to install alias")
|
return errors.New("failed to install alias")
|
||||||
}
|
}
|
||||||
@@ -510,15 +188,50 @@ func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientCon
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func installExtension(extm *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||||
|
deps := make(map[string]struct{})
|
||||||
|
for _, ext := range extm.ExtCommand {
|
||||||
|
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
|
||||||
|
sliverMenu := con.App.Menu(constants.ImplantMenu).Root()
|
||||||
|
for dep := range deps {
|
||||||
|
if extensions.CmdExists(dep, sliverMenu) {
|
||||||
|
continue // Dependency is already installed
|
||||||
|
}
|
||||||
|
err := installExtensionPackageByName(dep, clientConfig, con)
|
||||||
|
if err != nil {
|
||||||
|
con.PrintErrorf("Failed to install extension dependency '%s': %s", dep, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := installExtensionPackageByName(ext.CommandName, clientConfig, con)
|
||||||
|
if err != nil {
|
||||||
|
con.PrintErrorf("Failed to install extension '%s': %s", ext.CommandName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const maxDepDepth = 10 // Arbitrary recursive limit for dependencies
|
const maxDepDepth = 10 // Arbitrary recursive limit for dependencies
|
||||||
|
|
||||||
func resolveExtensionPackageDependencies(pkg *pkgCacheEntry, deps map[string]*pkgCacheEntry, pendingPackages map[string]string) error {
|
func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||||
for _, multiExt := range pkg.Extension.ExtCommand {
|
var entry *pkgCacheEntry
|
||||||
|
pkgCache.Range(func(key, value interface{}) bool {
|
||||||
|
cacheEntry := value.(pkgCacheEntry)
|
||||||
|
if cacheEntry.Pkg.CommandName == name {
|
||||||
|
entry = &cacheEntry
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if entry == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, multiExt := range entry.Extension.ExtCommand {
|
||||||
if multiExt.DependsOn == "" {
|
if multiExt.DependsOn == "" {
|
||||||
continue // Avoid adding empty dependency
|
continue // Avoid adding empty dependency
|
||||||
}
|
}
|
||||||
|
|
||||||
if multiExt.DependsOn == pkg.Extension.Name {
|
if multiExt.DependsOn == name {
|
||||||
continue // Avoid infinite loop of something that depends on itself
|
continue // Avoid infinite loop of something that depends on itself
|
||||||
}
|
}
|
||||||
// We also need to look out for circular dependencies, so if we've already
|
// We also need to look out for circular dependencies, so if we've already
|
||||||
@@ -526,28 +239,24 @@ func resolveExtensionPackageDependencies(pkg *pkgCacheEntry, deps map[string]*pk
|
|||||||
if _, ok := deps[multiExt.DependsOn]; ok {
|
if _, ok := deps[multiExt.DependsOn]; ok {
|
||||||
continue // Already resolved
|
continue // Already resolved
|
||||||
}
|
}
|
||||||
// Check to make sure we are not already going to install a package with this name
|
|
||||||
if _, ok := pendingPackages[multiExt.DependsOn]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if maxDepDepth < len(deps) {
|
if maxDepDepth < len(deps) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Figure out what package we need for the dependency
|
deps[multiExt.DependsOn] = struct{}{}
|
||||||
dependencyEntry, err := getPackageForCommand(multiExt.DependsOn, "", "")
|
resolveExtensionPackageDependencies(multiExt.DependsOn, deps, clientConfig, con)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not resolve dependency %s for %s: %s", multiExt.DependsOn, pkg.Extension.Name, err)
|
|
||||||
}
|
|
||||||
deps[multiExt.DependsOn] = dependencyEntry
|
|
||||||
err = resolveExtensionPackageDependencies(dependencyEntry, deps, pendingPackages)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func installExtensionPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
|
||||||
|
var entry *pkgCacheEntry
|
||||||
|
pkgCache.Range(func(key, value interface{}) bool {
|
||||||
|
cacheEntry := value.(pkgCacheEntry)
|
||||||
|
if cacheEntry.Pkg.CommandName == name {
|
||||||
|
entry = &cacheEntry
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
return errors.New("package not found")
|
return errors.New("package not found")
|
||||||
}
|
}
|
||||||
@@ -593,7 +302,7 @@ func installExtensionPackage(entry *pkgCacheEntry, promptToOverwrite bool, clien
|
|||||||
|
|
||||||
con.Printf(console.Clearln + "\r") // Clear download message
|
con.Printf(console.Clearln + "\r") // Clear download message
|
||||||
|
|
||||||
extensions.InstallFromDir(tmpFile.Name(), promptToOverwrite, con, true)
|
extensions.InstallFromDir(tmpFile.Name(), con, true)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -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")
|
|
||||||
}
|
|
||||||
@@ -19,8 +19,6 @@ package armory
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -69,18 +67,6 @@ type armoryPkgResponse struct {
|
|||||||
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
|
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculatePackageHash(pkg *ArmoryPackage) string {
|
|
||||||
if pkg == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
hasher := sha256.New()
|
|
||||||
// Hash some of the things that make the package unique
|
|
||||||
packageIdentifier := []byte(pkg.RepoURL + pkg.PublicKey + pkg.ArmoryName + pkg.CommandName)
|
|
||||||
hasher.Write(packageIdentifier)
|
|
||||||
return fmt.Sprintf("%x", hasher.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Default Parsers for Self-Hosted Armories
|
// Default Parsers for Self-Hosted Armories
|
||||||
//
|
//
|
||||||
@@ -93,10 +79,7 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
resp, body, _ := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, errors.New("api returned non-200 status code")
|
return nil, errors.New("api returned non-200 status code")
|
||||||
}
|
}
|
||||||
@@ -128,45 +111,9 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Populate armory name and ID information for assets
|
|
||||||
for _, bundle := range armoryIndex.Bundles {
|
|
||||||
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
|
|
||||||
}
|
|
||||||
for _, alias := range armoryIndex.Aliases {
|
|
||||||
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
|
|
||||||
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
|
||||||
if cached := packageCacheLookupByCmdAndArmory(alias.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
|
|
||||||
// A package with this name is in the cache, so we will remove it here so that the latest version is added
|
|
||||||
// when the index is parsed
|
|
||||||
pkgCache.Delete(cached.ID)
|
|
||||||
}
|
|
||||||
alias.ID = calculatePackageHash(alias)
|
|
||||||
}
|
|
||||||
for _, extension := range armoryIndex.Extensions {
|
|
||||||
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
|
|
||||||
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
|
||||||
if cached := packageCacheLookupByCmdAndArmory(extension.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
|
|
||||||
// A package with this name is in the cache, so we will remove it here so that the latest version is added
|
|
||||||
// when the index is parsed
|
|
||||||
pkgCache.Delete(cached.ID)
|
|
||||||
}
|
|
||||||
extension.ID = calculatePackageHash(extension)
|
|
||||||
}
|
|
||||||
return armoryIndex, nil
|
return armoryIndex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodePackageSignature(sigString string) ([]byte, error) {
|
|
||||||
// Base64 decode the signature
|
|
||||||
decodedData := make([]byte, base64.StdEncoding.DecodedLen(len(sigString)))
|
|
||||||
_, err := base64.StdEncoding.Decode(decodedData, []byte(sigString))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// If decodedData does not end up being base64.StdEncoding.DecodedLen(len(data)) bytes long, trim the null bytes
|
|
||||||
decodedData = bytes.Trim(decodedData, "\x00")
|
|
||||||
return decodedData, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url
|
// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url
|
||||||
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||||
var publicKey minisign.PublicKey
|
var publicKey minisign.PublicKey
|
||||||
@@ -175,29 +122,25 @@ func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *Armory
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, body, err := httpRequest(clientConfig, armoryPkg.RepoURL, armoryConfig, http.Header{})
|
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, nil, fmt.Errorf("api returned non-200 status code (%d)", resp.StatusCode)
|
return nil, nil, errors.New("api returned non-200 status code")
|
||||||
}
|
}
|
||||||
pkgResp := &armoryPkgResponse{}
|
pkgResp := &armoryPkgResponse{}
|
||||||
err = json.Unmarshal(body, pkgResp)
|
err = json.Unmarshal(body, pkgResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
decodedSig, err := decodePackageSignature(pkgResp.Minisig)
|
sig, err := parsePkgMinsig([]byte(pkgResp.Minisig))
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
sig, err := parsePkgMinsig(decodedSig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
var tarGz []byte
|
var tarGz []byte
|
||||||
if !sigOnly {
|
if !sigOnly {
|
||||||
tarGzURL, err := url.Parse(pkgResp.TarGzURL)
|
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -299,20 +242,6 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Populate armory name and ID information for assets
|
|
||||||
for _, bundle := range armoryIndex.Bundles {
|
|
||||||
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
|
|
||||||
}
|
|
||||||
for _, alias := range armoryIndex.Aliases {
|
|
||||||
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
|
|
||||||
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
|
||||||
alias.ID = calculatePackageHash(alias)
|
|
||||||
}
|
|
||||||
for _, extension := range armoryIndex.Extensions {
|
|
||||||
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
|
|
||||||
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
|
||||||
extension.ID = calculatePackageHash(extension)
|
|
||||||
}
|
|
||||||
return armoryIndex, nil
|
return armoryIndex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,12 +436,6 @@ func downloadRequest(clientConfig ArmoryHTTPConfig, reqURL string, armoryConfig
|
|||||||
"Accept": {"application/octet-stream"},
|
"Accept": {"application/octet-stream"},
|
||||||
}
|
}
|
||||||
resp, body, err := httpRequest(clientConfig, reqURL, armoryConfig, downloadHdr)
|
resp, body, err := httpRequest(clientConfig, reqURL, armoryConfig, downloadHdr)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp == nil {
|
|
||||||
return nil, fmt.Errorf("invalid response when downloading %s, try again later", reqURL)
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||||
return nil, fmt.Errorf("error downloading asset: http %d", resp.StatusCode)
|
return nil, fmt.Errorf("error downloading asset: http %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ArmorySearchCmd - Search for packages by name
|
// ArmorySearchCmd - Search for packages by name
|
||||||
func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
con.PrintInfof("Refreshing package cache ... ")
|
con.PrintInfof("Refreshing package cache ... ")
|
||||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||||
refresh(clientConfig)
|
refresh(clientConfig)
|
||||||
@@ -47,7 +47,7 @@ func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
aliases, exts := packageManifestsInCache()
|
aliases, exts := packagesInCache()
|
||||||
matchedAliases := []*alias.AliasManifest{}
|
matchedAliases := []*alias.AliasManifest{}
|
||||||
for _, a := range aliases {
|
for _, a := range aliases {
|
||||||
if nameExpr.MatchString(a.CommandName) {
|
if nameExpr.MatchString(a.CommandName) {
|
||||||
|
|||||||
+34
-294
@@ -19,127 +19,56 @@ package armory
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
"github.com/bishopfox/sliver/client/command/alias"
|
"github.com/bishopfox/sliver/client/command/alias"
|
||||||
"github.com/bishopfox/sliver/client/command/extensions"
|
"github.com/bishopfox/sliver/client/command/extensions"
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type VersionInformation struct {
|
|
||||||
OldVersion string
|
|
||||||
NewVersion string
|
|
||||||
ArmoryName string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PackageType uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
AliasPackage PackageType = iota
|
|
||||||
ExtensionPackage
|
|
||||||
)
|
|
||||||
|
|
||||||
type UpdateIdentifier struct {
|
|
||||||
Type PackageType
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArmoryUpdateCmd - Update all installed extensions/aliases
|
// ArmoryUpdateCmd - Update all installed extensions/aliases
|
||||||
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var selectedUpdates []UpdateIdentifier
|
|
||||||
var err error
|
|
||||||
|
|
||||||
con.PrintInfof("Refreshing package cache ... ")
|
con.PrintInfof("Refreshing package cache ... ")
|
||||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||||
refresh(clientConfig)
|
refresh(clientConfig)
|
||||||
con.Printf(console.Clearln + "\r")
|
con.Printf(console.Clearln + "\r")
|
||||||
|
|
||||||
armoryName, err := cmd.Flags().GetString("armory")
|
// Aliases
|
||||||
if err != nil {
|
aliasUpdates := checkForAliasUpdates(clientConfig, con)
|
||||||
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err)
|
if 0 < len(aliasUpdates) {
|
||||||
return
|
con.PrintInfof("%d alias(es) out of date: %s\n", len(aliasUpdates), strings.Join(aliasUpdates, ", "))
|
||||||
}
|
for _, aliasName := range aliasUpdates {
|
||||||
|
err := installAliasPackageByName(aliasName, clientConfig, con)
|
||||||
// Find PK for the armory name
|
if err != nil {
|
||||||
armoryPK := getArmoryPublicKey(armoryName)
|
con.PrintErrorf("Failed to update %s: %s\n", aliasName, err)
|
||||||
|
}
|
||||||
// If the armory with the name is not found, print a warning
|
|
||||||
if cmd.Flags().Changed("armory") && armoryPK == "" {
|
|
||||||
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check packages for updates
|
|
||||||
aliasUpdates := checkForAliasUpdates(armoryPK)
|
|
||||||
extUpdates := checkForExtensionUpdates(armoryPK)
|
|
||||||
|
|
||||||
// Display a table of results
|
|
||||||
if len(aliasUpdates) > 0 || len(extUpdates) > 0 {
|
|
||||||
updateKeys := sortUpdateIdentifiers(aliasUpdates, extUpdates)
|
|
||||||
displayAvailableUpdates(con, updateKeys, aliasUpdates, extUpdates)
|
|
||||||
selectedUpdates, err = getUpdatesFromUser(con, updateKeys)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf(err.Error() + "\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(selectedUpdates) == 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
con.PrintSuccessf("All packages are up to date")
|
con.PrintInfof("All aliases up to date!\n")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, update := range selectedUpdates {
|
// Extensions
|
||||||
switch update.Type {
|
extUpdates := checkForExtensionUpdates(clientConfig, con)
|
||||||
case AliasPackage:
|
if 0 < len(extUpdates) {
|
||||||
aliasVersionInfo, ok := aliasUpdates[update.Name]
|
con.PrintInfof("%d extension(s) out of date: %s\n", len(extUpdates), strings.Join(extUpdates, ", "))
|
||||||
if !ok {
|
for _, extName := range extUpdates {
|
||||||
continue
|
err := installExtensionPackageByName(extName, clientConfig, con)
|
||||||
}
|
|
||||||
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, aliasVersionInfo.NewVersion)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("Could not get update package for alias %s: %s\n", update.Name, err)
|
con.PrintErrorf("Failed to update %s: %s\n", extName, err)
|
||||||
// Keep going because other packages might not encounter errors
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
err = installAliasPackage(updatedPackage, false, clientConfig, con)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Failed to update %s: %s\n", update.Name, err)
|
|
||||||
}
|
|
||||||
case ExtensionPackage:
|
|
||||||
extVersionInfo, ok := extUpdates[update.Name]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, extVersionInfo.NewVersion)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Could not get update package for extension %s: %s\n", update.Name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = installExtensionPackage(updatedPackage, false, clientConfig, con)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Failed to update %s: %s\n", update.Name, err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
con.PrintInfof("All extensions up to date!\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkForAliasUpdates(armoryPK string) map[string]VersionInformation {
|
func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
|
||||||
cachedAliases, _ := packageManifestsInCache()
|
cachedAliases, _ := packagesInCache()
|
||||||
results := make(map[string]VersionInformation)
|
results := []string{}
|
||||||
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
|
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
|
||||||
data, err := os.ReadFile(aliasManifestPath)
|
data, err := os.ReadFile(aliasManifestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -150,31 +79,19 @@ func checkForAliasUpdates(armoryPK string) map[string]VersionInformation {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, latestAlias := range cachedAliases {
|
for _, latestAlias := range cachedAliases {
|
||||||
/*
|
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
|
||||||
We used to check if the version identifiers were different between the installed version and the
|
// the armory differs at all from the local version, the extension is out of date.
|
||||||
version in the armory. This worked when we had one armory, but with multiple potential armories,
|
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version != localManifest.Version {
|
||||||
we have to have some type of versioning. We do not have an official versioning scheme for packages,
|
results = append(results, localManifest.CommandName)
|
||||||
so we will rely on the author of the package incrementing the version identifier somehow to determine
|
|
||||||
if a package is newer.
|
|
||||||
*/
|
|
||||||
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version > localManifest.Version {
|
|
||||||
if latestAlias.ArmoryPK == armoryPK || armoryPK == "" {
|
|
||||||
results[localManifest.CommandName] = VersionInformation{
|
|
||||||
OldVersion: localManifest.Version,
|
|
||||||
NewVersion: latestAlias.Version,
|
|
||||||
ArmoryName: latestAlias.ArmoryName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation {
|
func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
|
||||||
_, cachedExtensions := packageManifestsInCache()
|
_, cachedExtensions := packagesInCache()
|
||||||
// Return a map of extension name to minimum version to install
|
results := []string{}
|
||||||
results := make(map[string]VersionInformation)
|
|
||||||
for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
|
for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
|
||||||
data, err := os.ReadFile(extManifestPath)
|
data, err := os.ReadFile(extManifestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -185,190 +102,13 @@ func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, latestExt := range cachedExtensions {
|
for _, latestExt := range cachedExtensions {
|
||||||
/*
|
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
|
||||||
We used to check if the version identifiers were different between the installed version and the
|
// the armory differs at all from the local version, the extension is out of date.
|
||||||
version in the armory. This worked when we had one armory, but with multiple potential armories,
|
if latestExt.Name == localManifest.Name && latestExt.Version != localManifest.Version {
|
||||||
we have to have some type of versioning. We do not have an official versioning scheme for packages,
|
results = append(results, localManifest.Name)
|
||||||
so we will rely on the author of the package incrementing the version identifier somehow to determine
|
|
||||||
if a package is newer.
|
|
||||||
*/
|
|
||||||
if latestExt.Name == localManifest.Name && latestExt.Version > localManifest.Version {
|
|
||||||
if latestExt.ArmoryPK == armoryPK || armoryPK == "" {
|
|
||||||
results[localManifest.Name] = VersionInformation{
|
|
||||||
OldVersion: localManifest.Version,
|
|
||||||
NewVersion: latestExt.Version,
|
|
||||||
ArmoryName: latestExt.ArmoryName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortUpdateIdentifiers(aliasUpdates, extensionUpdates map[string]VersionInformation) []UpdateIdentifier {
|
|
||||||
/*
|
|
||||||
This function helps us keep updates straight when the user chooses from them. Just in case
|
|
||||||
an alias and an extension exist with the same name, we cannot simply combine the two maps.
|
|
||||||
|
|
||||||
We will assume that no two aliases and no two extensions have the same name.
|
|
||||||
*/
|
|
||||||
|
|
||||||
result := []UpdateIdentifier{}
|
|
||||||
|
|
||||||
aliasNames := util.Keys(aliasUpdates)
|
|
||||||
extensionNames := util.Keys(extensionUpdates)
|
|
||||||
for _, name := range aliasNames {
|
|
||||||
result = append(result, UpdateIdentifier{
|
|
||||||
Type: AliasPackage,
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, name := range extensionNames {
|
|
||||||
result = append(result, UpdateIdentifier{
|
|
||||||
Type: ExtensionPackage,
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(result, func(i, j int) bool {
|
|
||||||
return result[i].Name < result[j].Name
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func displayAvailableUpdates(con *console.SliverClient, updateKeys []UpdateIdentifier, aliasUpdates, extensionUpdates map[string]VersionInformation) {
|
|
||||||
var (
|
|
||||||
aliasSuffix string
|
|
||||||
extensionSuffix string
|
|
||||||
title string = "Available Updates (%d alias%s, %d extension%s)"
|
|
||||||
)
|
|
||||||
|
|
||||||
tw := table.NewWriter()
|
|
||||||
tw.SetAutoIndex(true)
|
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
|
||||||
if len(aliasUpdates) != 1 {
|
|
||||||
aliasSuffix = "es"
|
|
||||||
}
|
|
||||||
if len(extensionUpdates) != 1 {
|
|
||||||
extensionSuffix = "s"
|
|
||||||
}
|
|
||||||
tw.SetTitle(console.Bold + fmt.Sprintf(title, len(aliasUpdates), aliasSuffix, len(extensionUpdates), extensionSuffix) + console.Normal)
|
|
||||||
tw.AppendHeader(table.Row{
|
|
||||||
"Package Name",
|
|
||||||
"Package Type",
|
|
||||||
"Installed Version",
|
|
||||||
"Available Version",
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, key := range updateKeys {
|
|
||||||
var (
|
|
||||||
packageName string
|
|
||||||
packageType string
|
|
||||||
packageVersion VersionInformation
|
|
||||||
ok bool
|
|
||||||
)
|
|
||||||
switch key.Type {
|
|
||||||
case AliasPackage:
|
|
||||||
packageVersion, ok = aliasUpdates[key.Name]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
packageName = key.Name
|
|
||||||
packageType = "Alias"
|
|
||||||
case ExtensionPackage:
|
|
||||||
packageVersion, ok = extensionUpdates[key.Name]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
packageName = key.Name
|
|
||||||
packageType = "Extension"
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tw.AppendRow(table.Row{
|
|
||||||
packageName,
|
|
||||||
packageType,
|
|
||||||
packageVersion.OldVersion,
|
|
||||||
fmt.Sprintf("%s (Armory: %s)", packageVersion.NewVersion, packageVersion.ArmoryName),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
con.Printf("%s\n\n", tw.Render())
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUpdatesFromUser(con *console.SliverClient, updateKeys []UpdateIdentifier) (chosenUpdates []UpdateIdentifier, selectionError error) {
|
|
||||||
chosenUpdates = []UpdateIdentifier{}
|
|
||||||
|
|
||||||
var updateResponse string
|
|
||||||
|
|
||||||
con.Println("You can apply all, none, or some updates.\nTo apply some updates, specify the number of a single update, a range (1-3), or a combination of the two (1, 3-5, 7)\n")
|
|
||||||
err := survey.AskOne(&survey.Input{
|
|
||||||
Message: "Which updates would you like to apply? [A]ll, [N]one, or some:",
|
|
||||||
}, &updateResponse)
|
|
||||||
if err != nil {
|
|
||||||
// We do not need to set selectionError because an error here means the same thing as responding with "none"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
updateResponse = strings.ToLower(updateResponse)
|
|
||||||
updateResponse = strings.Replace(updateResponse, " ", "", -1)
|
|
||||||
if updateResponse == "n" || updateResponse == "none" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if updateResponse == "a" || updateResponse == "all" {
|
|
||||||
chosenUpdates = updateKeys
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
selections := strings.Split(updateResponse, ",")
|
|
||||||
|
|
||||||
for _, selection := range selections {
|
|
||||||
// Handle a range
|
|
||||||
if strings.Contains(selection, "-") {
|
|
||||||
rangeParts := strings.Split(selection, "-")
|
|
||||||
start, err := strconv.Atoi(rangeParts[0])
|
|
||||||
if err != nil {
|
|
||||||
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
end, err := strconv.Atoi(rangeParts[1])
|
|
||||||
if err != nil {
|
|
||||||
selectionError = fmt.Errorf("%s is not a valid range", rangeParts[1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Adjust for the 0 indexed slice we are working with
|
|
||||||
start -= 1
|
|
||||||
end -= 1
|
|
||||||
if start < 0 {
|
|
||||||
start = 0
|
|
||||||
}
|
|
||||||
if start > end {
|
|
||||||
selectionError = fmt.Errorf("%s is not a valid range", selection)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if end >= len(updateKeys) {
|
|
||||||
end = len(updateKeys) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := start; i <= end; i++ {
|
|
||||||
chosenUpdates = append(chosenUpdates, updateKeys[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Single entry
|
|
||||||
index, err := strconv.Atoi(selection)
|
|
||||||
if err != nil {
|
|
||||||
selectionError = fmt.Errorf("%s is not a valid range", selection)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
index -= 1
|
|
||||||
if index >= 0 && index < len(updateKeys) {
|
|
||||||
chosenUpdates = append(chosenUpdates, updateKeys[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -21,13 +21,14 @@ package backdoor
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BackdoorCmd - Command to inject implant code into an existing binary.
|
// BackdoorCmd - Command to inject implant code into an existing binary
|
||||||
func BackdoorCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BeaconsCmd - Display/interact with beacons
|
// BeaconsCmd - Display/interact with beacons
|
||||||
func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
killFlag, _ := cmd.Flags().GetString("kill")
|
killFlag, _ := cmd.Flags().GetString("kill")
|
||||||
killAll, _ := cmd.Flags().GetBool("kill-all")
|
killAll, _ := cmd.Flags().GetBool("kill-all")
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintBeacons - Display a list of beacons
|
// PrintBeacons - Display a list of beacons
|
||||||
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) {
|
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
|
||||||
if len(beacons) == 0 {
|
if len(beacons) == 0 {
|
||||||
con.PrintInfof("No beacons 🙁\n")
|
con.PrintInfof("No beacons 🙁\n")
|
||||||
return
|
return
|
||||||
@@ -103,7 +103,7 @@ func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp
|
|||||||
con.Printf("%s\n", tw.Render())
|
con.Printf("%s\n", tw.Render())
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) table.Writer {
|
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) table.Writer {
|
||||||
width, _, err := term.GetSize(0)
|
width, _, err := term.GetSize(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
width = 999
|
width = 999
|
||||||
@@ -112,45 +112,20 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
|||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
wideTermWidth := con.Settings.SmallTermWidth < width
|
wideTermWidth := con.Settings.SmallTermWidth < width
|
||||||
windowsBeaconInList := false
|
|
||||||
for _, beacon := range beacons {
|
|
||||||
if beacon.OS == "windows" {
|
|
||||||
windowsBeaconInList = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if wideTermWidth {
|
if wideTermWidth {
|
||||||
if windowsBeaconInList {
|
tw.AppendHeader(table.Row{
|
||||||
tw.AppendHeader(table.Row{
|
"ID",
|
||||||
"ID",
|
"Name",
|
||||||
"Name",
|
"Tasks",
|
||||||
"Tasks",
|
"Transport",
|
||||||
"Transport",
|
"Remote Address",
|
||||||
"Remote Address",
|
"Hostname",
|
||||||
"Hostname",
|
"Username",
|
||||||
"Username",
|
"Operating System",
|
||||||
"Process (PID)",
|
"Locale",
|
||||||
"Integrity",
|
"Last Check-in",
|
||||||
"Operating System",
|
"Next Check-in",
|
||||||
"Locale",
|
})
|
||||||
"Last Check-in",
|
|
||||||
"Next Check-in",
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
tw.AppendHeader(table.Row{
|
|
||||||
"ID",
|
|
||||||
"Name",
|
|
||||||
"Tasks",
|
|
||||||
"Transport",
|
|
||||||
"Remote Address",
|
|
||||||
"Hostname",
|
|
||||||
"Username",
|
|
||||||
"Process (PID)",
|
|
||||||
"Operating System",
|
|
||||||
"Locale",
|
|
||||||
"Last Check-in",
|
|
||||||
"Next Check-in",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
tw.AppendHeader(table.Row{
|
tw.AppendHeader(table.Row{
|
||||||
"ID",
|
"ID",
|
||||||
@@ -170,9 +145,6 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
|||||||
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
|
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
|
||||||
color = console.Green
|
color = console.Green
|
||||||
}
|
}
|
||||||
if beacon.Integrity == "" {
|
|
||||||
beacon.Integrity = "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need a slice of strings so we can apply filters
|
// We need a slice of strings so we can apply filters
|
||||||
var rowEntries []string
|
var rowEntries []string
|
||||||
@@ -186,19 +158,11 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
|||||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
|
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
|
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||||
fmt.Sprintf(color+"%s (%d)"+console.Normal, beacon.Filename, beacon.PID),
|
|
||||||
}
|
|
||||||
|
|
||||||
if windowsBeaconInList {
|
|
||||||
rowEntries = append(rowEntries, fmt.Sprintf(color+"%s"+console.Normal, beacon.Integrity))
|
|
||||||
}
|
|
||||||
|
|
||||||
rowEntries = append(rowEntries, []string{
|
|
||||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Locale),
|
fmt.Sprintf(color+"%s"+console.Normal, beacon.Locale),
|
||||||
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
|
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
|
||||||
con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true),
|
con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true),
|
||||||
}...)
|
}
|
||||||
} else {
|
} else {
|
||||||
rowEntries = []string{
|
rowEntries = []string{
|
||||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -43,7 +43,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
|
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
|
||||||
func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
|
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
|
||||||
grpcCtx, cancel := con.GrpcContext(nil)
|
grpcCtx, cancel := con.GrpcContext(nil)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||||
@@ -102,7 +102,7 @@ func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
|
|||||||
return nil, ErrNoSelection
|
return nil, ErrNoSelection
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBeacon(con *console.SliverClient, beaconID string) (*clientpb.Beacon, error) {
|
func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
|
||||||
grpcCtx, cancel := con.GrpcContext(nil)
|
grpcCtx, cancel := con.GrpcContext(nil)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||||
@@ -120,7 +120,7 @@ func GetBeacon(con *console.SliverClient, beaconID string) (*clientpb.Beacon, er
|
|||||||
return nil, ErrBeaconNotFound
|
return nil, ErrBeaconNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBeacons(con *console.SliverClient) (*clientpb.Beacons, error) {
|
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
|
||||||
grpcCtx, cancel := con.GrpcContext(nil)
|
grpcCtx, cancel := con.GrpcContext(nil)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BeaconsPruneCmd - Prune stale beacons automatically
|
// BeaconsPruneCmd - Prune stale beacons automatically
|
||||||
func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
duration, _ := cmd.Flags().GetString("duration")
|
duration, _ := cmd.Flags().GetString("duration")
|
||||||
pruneDuration, err := time.ParseDuration(duration)
|
pruneDuration, err := time.ParseDuration(duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BeaconsRmCmd - Display/interact with beacons
|
// BeaconsRmCmd - Display/interact with beacons
|
||||||
func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
beacon, err := SelectBeacon(con)
|
beacon, err := SelectBeacon(con)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BeaconsWatchCmd - Watch your beacons in real-ish time
|
// BeaconsWatchCmd - Watch your beacons in real-ish time
|
||||||
func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
done := waitForInput()
|
done := waitForInput()
|
||||||
defer func() {
|
defer func() {
|
||||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||||
|
|||||||
@@ -23,16 +23,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildersCmd - List external builders.
|
// BuildersCmd - List external builders
|
||||||
func BuildersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
|
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s", err)
|
con.PrintErrorf("%s", err)
|
||||||
@@ -45,7 +46,7 @@ func BuildersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverClient) {
|
func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
tw.AppendHeader(table.Row{
|
tw.AppendHeader(table.Row{
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -21,12 +21,8 @@ package c2profiles
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
@@ -43,7 +39,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// C2ProfileCmd list available http profiles
|
// C2ProfileCmd list available http profiles
|
||||||
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
profileName, _ := cmd.Flags().GetString("name")
|
profileName, _ := cmd.Flags().GetString("name")
|
||||||
|
|
||||||
if profileName == constants.DefaultC2Profile {
|
if profileName == constants.DefaultC2Profile {
|
||||||
@@ -65,7 +61,8 @@ func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
|||||||
PrintC2Profiles(profile, con)
|
PrintC2Profiles(profile, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
|
protocols := []string{constants.HttpStr, constants.HttpsStr}
|
||||||
profileName, _ := cmd.Flags().GetString("name")
|
profileName, _ := cmd.Flags().GetString("name")
|
||||||
if profileName == "" {
|
if profileName == "" {
|
||||||
con.PrintErrorf("Invalid c2 profile name\n")
|
con.PrintErrorf("Invalid c2 profile name\n")
|
||||||
@@ -101,212 +98,29 @@ func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
|||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
confirm := false
|
||||||
|
prompt := &survey.Confirm{Message: "Restart HTTP/S jobs?"}
|
||||||
func ExportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
survey.AskOne(prompt, &confirm)
|
||||||
|
if confirm {
|
||||||
filepath, _ := cmd.Flags().GetString("file")
|
var restartJobReq clientpb.RestartJobReq
|
||||||
if filepath == "" {
|
jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
|
||||||
con.PrintErrorf("Missing file path\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
profileName, _ := cmd.Flags().GetString("name")
|
|
||||||
if profileName == "" {
|
|
||||||
con.PrintErrorf("Invalid c2 profile name\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if profileName == constants.DefaultC2Profile {
|
|
||||||
httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(context.Background(), &commonpb.Empty{})
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("failed to fetch HTTP C2 profiles: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(httpC2Profiles.Configs) != 1 {
|
|
||||||
profileName = selectC2Profile(httpC2Profiles.Configs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: profileName})
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := C2ConfigToJSON(profileName, profile)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonProfile, err := json.Marshal(config)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.WriteFile(filepath, jsonProfile, 0644)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
con.Println(profileName, "C2 profile exported to ", filepath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|
||||||
|
|
||||||
// load template to use as starting point
|
|
||||||
template, err := cmd.Flags().GetString("template")
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
profileName, _ := cmd.Flags().GetString("name")
|
|
||||||
if profileName == "" {
|
|
||||||
con.PrintErrorf("Invalid c2 profile name\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: template})
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := C2ConfigToJSON(profileName, profile)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// read urls files and replace segments
|
|
||||||
filepath, err := cmd.Flags().GetString("file")
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
urlsFile, err := os.Open(filepath)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fileContent, err := io.ReadAll(urlsFile)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
urls := strings.Split(string(fileContent), "\n")
|
|
||||||
|
|
||||||
jsonProfile, err := updateC2Profile(config, urls)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// save or display config
|
|
||||||
importC2Profile, err := cmd.Flags().GetBool("import")
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if importC2Profile {
|
|
||||||
httpC2ConfigReq := clientpb.HTTPC2ConfigReq{C2Config: C2ConfigToProtobuf(profileName, jsonProfile)}
|
|
||||||
_, err = con.Rpc.SaveHTTPC2Profile(context.Background(), &httpC2ConfigReq)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
con.Println("C2 profile generated and saved as ", profileName)
|
// reload jobs to include new profile
|
||||||
} else {
|
for _, job := range jobs.Active {
|
||||||
PrintC2Profiles(C2ConfigToProtobuf(profileName, jsonProfile), con)
|
if job != nil && slices.Contains(protocols, job.Name) {
|
||||||
}
|
restartJobReq.JobIDs = append(restartJobReq.JobIDs, job.ID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convert protobuf to json
|
_, err = con.Rpc.RestartJobs(context.Background(), &restartJobReq)
|
||||||
func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) (*assets.HTTPC2Config, error) {
|
if err != nil {
|
||||||
implantConfig := assets.HTTPC2ImplantConfig{
|
con.PrintErrorf("%s\n", err)
|
||||||
UserAgent: profile.ImplantConfig.UserAgent,
|
return
|
||||||
ChromeBaseVersion: int(profile.ImplantConfig.ChromeBaseVersion),
|
|
||||||
MacOSVersion: profile.ImplantConfig.MacOSVersion,
|
|
||||||
NonceQueryArgChars: profile.ImplantConfig.NonceQueryArgChars,
|
|
||||||
NonceQueryLength: int(profile.ImplantConfig.NonceQueryLength),
|
|
||||||
NonceMode: profile.ImplantConfig.NonceMode,
|
|
||||||
MaxFileGen: int(profile.ImplantConfig.MaxFileGen),
|
|
||||||
MinFileGen: int(profile.ImplantConfig.MinFileGen),
|
|
||||||
MaxPathGen: int(profile.ImplantConfig.MaxPathGen),
|
|
||||||
MinPathGen: int(profile.ImplantConfig.MinFileGen),
|
|
||||||
MaxPathLength: int(profile.ImplantConfig.MaxPathLength),
|
|
||||||
MinPathLength: int(profile.ImplantConfig.MinPathLength),
|
|
||||||
Extensions: profile.ImplantConfig.Extensions,
|
|
||||||
}
|
|
||||||
|
|
||||||
var headers []assets.NameValueProbability
|
|
||||||
for _, header := range profile.ImplantConfig.Headers {
|
|
||||||
headers = append(headers, assets.NameValueProbability{
|
|
||||||
Name: header.Name,
|
|
||||||
Value: header.Value,
|
|
||||||
Probability: int(header.Probability),
|
|
||||||
Method: header.Method,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
implantConfig.Headers = headers
|
|
||||||
|
|
||||||
var urlParameters []assets.NameValueProbability
|
|
||||||
for _, urlParameter := range profile.ImplantConfig.ExtraURLParameters {
|
|
||||||
urlParameters = append(urlParameters, assets.NameValueProbability{
|
|
||||||
Name: urlParameter.Name,
|
|
||||||
Value: urlParameter.Value,
|
|
||||||
Probability: int(urlParameter.Probability),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
implantConfig.URLParameters = urlParameters
|
|
||||||
|
|
||||||
var (
|
|
||||||
files []string
|
|
||||||
paths []string
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, pathSegment := range profile.ImplantConfig.PathSegments {
|
|
||||||
if pathSegment.IsFile {
|
|
||||||
files = append(files, pathSegment.Value)
|
|
||||||
} else {
|
|
||||||
paths = append(paths, pathSegment.Value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
implantConfig.Files = files
|
|
||||||
implantConfig.Paths = paths
|
|
||||||
|
|
||||||
var serverHeaders []assets.NameValueProbability
|
|
||||||
for _, header := range profile.ServerConfig.Headers {
|
|
||||||
serverHeaders = append(serverHeaders, assets.NameValueProbability{
|
|
||||||
Name: header.Name,
|
|
||||||
Value: header.Value,
|
|
||||||
Probability: int(header.Probability),
|
|
||||||
Method: header.Method,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverCookies []string
|
|
||||||
for _, cookie := range profile.ServerConfig.Cookies {
|
|
||||||
serverCookies = append(serverCookies, cookie.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverConfig := assets.HTTPC2ServerConfig{
|
|
||||||
RandomVersionHeaders: profile.ServerConfig.RandomVersionHeaders,
|
|
||||||
Headers: serverHeaders,
|
|
||||||
Cookies: serverCookies,
|
|
||||||
}
|
|
||||||
|
|
||||||
config := assets.HTTPC2Config{
|
|
||||||
ImplantConfig: implantConfig,
|
|
||||||
ServerConfig: serverConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &config, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert json to protobuf
|
// convert json to protobuf
|
||||||
@@ -317,66 +131,101 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
|
|||||||
pathSegments := []*clientpb.HTTPC2PathSegment{}
|
pathSegments := []*clientpb.HTTPC2PathSegment{}
|
||||||
|
|
||||||
// files
|
// files
|
||||||
for _, file := range config.ImplantConfig.Files {
|
for _, poll := range config.ImplantConfig.PollFiles {
|
||||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
IsFile: true,
|
IsFile: true,
|
||||||
Value: file,
|
SegmentType: 0,
|
||||||
|
Value: poll,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range config.ImplantConfig.Paths {
|
for _, session := range config.ImplantConfig.SessionFiles {
|
||||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
IsFile: false,
|
IsFile: true,
|
||||||
Value: path,
|
SegmentType: 1,
|
||||||
|
Value: session,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, clientHeader := range config.ImplantConfig.Headers {
|
for _, close := range config.ImplantConfig.CloseFiles {
|
||||||
httpC2Headers = append(httpC2Headers, &clientpb.HTTPC2Header{
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
Method: clientHeader.Method,
|
IsFile: true,
|
||||||
Name: clientHeader.Name,
|
SegmentType: 2,
|
||||||
Value: clientHeader.Value,
|
Value: close,
|
||||||
Probability: int32(clientHeader.Probability),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, urlParameter := range config.ImplantConfig.URLParameters {
|
for _, stager := range config.ImplantConfig.StagerFiles {
|
||||||
httpC2UrlParameters = append(httpC2UrlParameters, &clientpb.HTTPC2URLParameter{
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
Method: urlParameter.Method,
|
IsFile: true,
|
||||||
Name: urlParameter.Name,
|
SegmentType: 3,
|
||||||
Value: urlParameter.Value,
|
Value: stager,
|
||||||
Probability: int32(urlParameter.Probability),
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// paths
|
||||||
|
for _, poll := range config.ImplantConfig.PollPaths {
|
||||||
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
|
IsFile: false,
|
||||||
|
SegmentType: 0,
|
||||||
|
Value: poll,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, session := range config.ImplantConfig.SessionPaths {
|
||||||
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
|
IsFile: false,
|
||||||
|
SegmentType: 1,
|
||||||
|
Value: session,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, close := range config.ImplantConfig.ClosePaths {
|
||||||
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
|
IsFile: false,
|
||||||
|
SegmentType: 2,
|
||||||
|
Value: close,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stager := range config.ImplantConfig.StagerPaths {
|
||||||
|
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||||
|
IsFile: false,
|
||||||
|
SegmentType: 3,
|
||||||
|
Value: stager,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
implantConfig := &clientpb.HTTPC2ImplantConfig{
|
implantConfig := &clientpb.HTTPC2ImplantConfig{
|
||||||
UserAgent: config.ImplantConfig.UserAgent,
|
UserAgent: config.ImplantConfig.UserAgent,
|
||||||
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
|
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
|
||||||
MacOSVersion: config.ImplantConfig.MacOSVersion,
|
MacOSVersion: config.ImplantConfig.MacOSVersion,
|
||||||
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
|
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
|
||||||
NonceQueryLength: int32(config.ImplantConfig.NonceQueryLength),
|
ExtraURLParameters: httpC2UrlParameters,
|
||||||
NonceMode: config.ImplantConfig.NonceMode,
|
Headers: httpC2Headers,
|
||||||
ExtraURLParameters: httpC2UrlParameters,
|
MaxFiles: int32(config.ImplantConfig.MaxFiles),
|
||||||
Headers: httpC2Headers,
|
MinFiles: int32(config.ImplantConfig.MinFiles),
|
||||||
MaxFileGen: int32(config.ImplantConfig.MaxFileGen),
|
MaxPaths: int32(config.ImplantConfig.MaxPaths),
|
||||||
MinFileGen: int32(config.ImplantConfig.MinFileGen),
|
MinPaths: int32(config.ImplantConfig.MinFiles),
|
||||||
MaxPathGen: int32(config.ImplantConfig.MaxPathGen),
|
StagerFileExtension: config.ImplantConfig.StagerFileExt,
|
||||||
MinPathGen: int32(config.ImplantConfig.MinPathGen),
|
PollFileExtension: config.ImplantConfig.PollFileExt,
|
||||||
MaxPathLength: int32(config.ImplantConfig.MaxPathLength),
|
StartSessionFileExtension: config.ImplantConfig.StartSessionFileExt,
|
||||||
MinPathLength: int32(config.ImplantConfig.MinPathLength),
|
SessionFileExtension: config.ImplantConfig.SessionFileExt,
|
||||||
Extensions: config.ImplantConfig.Extensions,
|
CloseFileExtension: config.ImplantConfig.CloseFileExt,
|
||||||
PathSegments: pathSegments,
|
PathSegments: pathSegments,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server Config
|
// Server Config
|
||||||
serverHeaders := []*clientpb.HTTPC2Header{}
|
serverHeaders := []*clientpb.HTTPC2Header{}
|
||||||
for _, serverHeader := range config.ServerConfig.Headers {
|
for _, serverHeader := range config.ServerConfig.Headers {
|
||||||
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
|
for _, method := range serverHeader.Methods {
|
||||||
Method: serverHeader.Method,
|
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
|
||||||
Name: serverHeader.Name,
|
Method: method,
|
||||||
Value: serverHeader.Value,
|
Name: serverHeader.Name,
|
||||||
Probability: int32(serverHeader.Probability),
|
Value: serverHeader.Value,
|
||||||
})
|
Probability: int32(serverHeader.Probability),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serverCookies := []*clientpb.HTTPC2Cookie{}
|
serverCookies := []*clientpb.HTTPC2Cookie{}
|
||||||
@@ -399,7 +248,7 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintImplantBuilds - Print the implant builds on the server
|
// PrintImplantBuilds - Print the implant builds on the server
|
||||||
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient) {
|
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverConsoleClient) {
|
||||||
|
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
@@ -419,7 +268,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
|
|||||||
|
|
||||||
var serverHeaders []string
|
var serverHeaders []string
|
||||||
for _, header := range profile.ServerConfig.Headers {
|
for _, header := range profile.ServerConfig.Headers {
|
||||||
serverHeaders = append(serverHeaders, header.Name)
|
serverHeaders = append(serverHeaders, header.Value)
|
||||||
}
|
}
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Server Headers",
|
"Server Headers",
|
||||||
@@ -444,7 +293,7 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
|
|||||||
|
|
||||||
var clientHeaders []string
|
var clientHeaders []string
|
||||||
for _, header := range profile.ImplantConfig.Headers {
|
for _, header := range profile.ImplantConfig.Headers {
|
||||||
clientHeaders = append(clientHeaders, header.Name)
|
clientHeaders = append(clientHeaders, header.Value)
|
||||||
}
|
}
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Client Headers",
|
"Client Headers",
|
||||||
@@ -475,65 +324,96 @@ func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient)
|
|||||||
"Nonce query arg chars",
|
"Nonce query arg chars",
|
||||||
profile.ImplantConfig.NonceQueryArgChars,
|
profile.ImplantConfig.NonceQueryArgChars,
|
||||||
})
|
})
|
||||||
tw.AppendRow(table.Row{
|
|
||||||
"Nonce query length",
|
|
||||||
profile.ImplantConfig.NonceQueryLength,
|
|
||||||
})
|
|
||||||
tw.AppendRow(table.Row{
|
|
||||||
"Nonce mode",
|
|
||||||
profile.ImplantConfig.NonceMode,
|
|
||||||
})
|
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Max files",
|
"Max files",
|
||||||
profile.ImplantConfig.MaxFileGen,
|
profile.ImplantConfig.MaxFiles,
|
||||||
})
|
})
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Min files",
|
"Min files",
|
||||||
profile.ImplantConfig.MinFileGen,
|
profile.ImplantConfig.MinFiles,
|
||||||
})
|
})
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Max paths",
|
"Max paths",
|
||||||
profile.ImplantConfig.MaxPathGen,
|
profile.ImplantConfig.MaxPaths,
|
||||||
})
|
})
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Min paths",
|
"Min paths",
|
||||||
profile.ImplantConfig.MinPathGen,
|
profile.ImplantConfig.MinPaths,
|
||||||
})
|
})
|
||||||
|
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Max path length",
|
"Stager file extension",
|
||||||
profile.ImplantConfig.MaxPathLength,
|
profile.ImplantConfig.StagerFileExtension,
|
||||||
})
|
})
|
||||||
|
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Min path length",
|
"Start session file extension",
|
||||||
profile.ImplantConfig.MinPathLength,
|
profile.ImplantConfig.StartSessionFileExtension,
|
||||||
})
|
})
|
||||||
|
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"File extensions",
|
"Session file extension",
|
||||||
strings.Join(profile.ImplantConfig.Extensions, ","),
|
profile.ImplantConfig.SessionFileExtension,
|
||||||
|
})
|
||||||
|
tw.AppendRow(table.Row{
|
||||||
|
"Poll file extension",
|
||||||
|
profile.ImplantConfig.PollFileExtension,
|
||||||
|
})
|
||||||
|
tw.AppendRow(table.Row{
|
||||||
|
"Close file extension",
|
||||||
|
profile.ImplantConfig.CloseFileExtension,
|
||||||
})
|
})
|
||||||
|
|
||||||
var (
|
var (
|
||||||
paths []string
|
pollPaths []string
|
||||||
files []string
|
pollFiles []string
|
||||||
|
sessionPaths []string
|
||||||
|
sessionFiles []string
|
||||||
|
closePaths []string
|
||||||
|
closeFiles []string
|
||||||
)
|
)
|
||||||
for _, segment := range profile.ImplantConfig.PathSegments {
|
for _, segment := range profile.ImplantConfig.PathSegments {
|
||||||
if segment.IsFile {
|
if segment.IsFile {
|
||||||
files = append(files, segment.Value)
|
switch segment.SegmentType {
|
||||||
|
case 0:
|
||||||
|
pollFiles = append(pollFiles, segment.Value)
|
||||||
|
case 1:
|
||||||
|
sessionFiles = append(sessionFiles, segment.Value)
|
||||||
|
case 2:
|
||||||
|
closeFiles = append(closeFiles, segment.Value)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
paths = append(paths, segment.Value)
|
switch segment.SegmentType {
|
||||||
|
case 0:
|
||||||
|
pollPaths = append(pollPaths, segment.Value)
|
||||||
|
case 1:
|
||||||
|
sessionPaths = append(sessionPaths, segment.Value)
|
||||||
|
case 2:
|
||||||
|
closePaths = append(closePaths, segment.Value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Paths",
|
"Poll paths",
|
||||||
strings.Join(paths[:], ",")[:50] + "...",
|
strings.Join(pollPaths[:], ","),
|
||||||
})
|
})
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
"Files",
|
"Poll files",
|
||||||
strings.Join(files[:], ",")[:50] + "...",
|
strings.Join(pollFiles[:], ","),
|
||||||
|
})
|
||||||
|
tw.AppendRow(table.Row{
|
||||||
|
"Session paths",
|
||||||
|
strings.Join(sessionPaths[:], ","),
|
||||||
|
})
|
||||||
|
tw.AppendRow(table.Row{
|
||||||
|
"Session files",
|
||||||
|
strings.Join(sessionFiles[:], ","),
|
||||||
|
})
|
||||||
|
tw.AppendRow(table.Row{
|
||||||
|
"Close paths",
|
||||||
|
strings.Join(closePaths[:], ","),
|
||||||
|
})
|
||||||
|
tw.AppendRow(table.Row{
|
||||||
|
"Close files",
|
||||||
|
strings.Join(closeFiles[:], ","),
|
||||||
})
|
})
|
||||||
|
|
||||||
con.Println(tw.Render())
|
con.Println(tw.Render())
|
||||||
@@ -555,59 +435,3 @@ func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
|
|||||||
|
|
||||||
return c2profile
|
return c2profile
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateC2Profile(template *assets.HTTPC2Config, urls []string) (*assets.HTTPC2Config, error) {
|
|
||||||
// update the template with the urls
|
|
||||||
|
|
||||||
var (
|
|
||||||
paths []string
|
|
||||||
filenames []string
|
|
||||||
extensions []string
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, urlPath := range urls {
|
|
||||||
parsedURL, err := url.Parse(urlPath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error parsing URL:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, file := path.Split(parsedURL.Path)
|
|
||||||
dir = strings.Trim(dir, "/")
|
|
||||||
if dir != "" {
|
|
||||||
paths = append(paths, strings.Split(dir, "/")...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if file != "" {
|
|
||||||
fileName := strings.TrimSuffix(file, filepath.Ext(file))
|
|
||||||
filenames = append(filenames, fileName)
|
|
||||||
ext := strings.TrimPrefix(filepath.Ext(file), ".")
|
|
||||||
if ext != "" {
|
|
||||||
extensions = append(extensions, ext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
slices.Sort(extensions)
|
|
||||||
extensions = slices.Compact(extensions)
|
|
||||||
|
|
||||||
slices.Sort(paths)
|
|
||||||
paths = slices.Compact(paths)
|
|
||||||
|
|
||||||
slices.Sort(filenames)
|
|
||||||
filenames = slices.Compact(filenames)
|
|
||||||
|
|
||||||
// 5 is arbitrarily used as a minimum value
|
|
||||||
if len(paths) < 5 {
|
|
||||||
return nil, fmt.Errorf("got %d paths need at least 5", len(paths))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(filenames) < 5 {
|
|
||||||
return nil, fmt.Errorf("got %d paths need at least 5", len(filenames))
|
|
||||||
}
|
|
||||||
|
|
||||||
template.ImplantConfig.Extensions = extensions
|
|
||||||
template.ImplantConfig.Paths = paths
|
|
||||||
template.ImplantConfig.Files = filenames
|
|
||||||
return template, nil
|
|
||||||
}
|
|
||||||
@@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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())
|
|
||||||
}
|
|
||||||
@@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
@@ -21,18 +21,19 @@ package command
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
client "github.com/bishopfox/sliver/client/console"
|
|
||||||
"github.com/reeflective/console"
|
"github.com/reeflective/console"
|
||||||
"github.com/rsteube/carapace"
|
"github.com/rsteube/carapace"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bind is a convenience function to bind flags to a given command.
|
const defaultTimeout = 60
|
||||||
|
|
||||||
|
// Flags is a convenience function to bind flags to a given command.
|
||||||
// name - The name of the flag set (can be empty).
|
// name - The name of the flag set (can be empty).
|
||||||
// cmd - The command to which the flags should be bound.
|
// cmd - The command to which the flags should be bound.
|
||||||
// flags - A function exposing the flag set through which flags are declared.
|
// flags - A function exposing the flag set through which flags are declared.
|
||||||
func Bind(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) {
|
func Flags(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) {
|
||||||
flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set.
|
flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set.
|
||||||
flags(flagSet) // Let the user bind any number of flags to it.
|
flags(flagSet) // Let the user bind any number of flags to it.
|
||||||
|
|
||||||
@@ -43,21 +44,22 @@ func Bind(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindFlagCompletions is a convenience function for adding completions to a command's flags.
|
// FlagComps is a convenience function for adding completions to a command's flags.
|
||||||
// cmd - The command owning the flags to complete.
|
// cmd - The command owning the flags to complete.
|
||||||
// bind - A function exposing a map["flag-name"]carapace.Action.
|
// bind - A function exposing a map["flag-name"]carapace.Action.
|
||||||
func BindFlagCompletions(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) {
|
func FlagComps(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) {
|
||||||
comps := make(carapace.ActionMap)
|
comps := make(carapace.ActionMap)
|
||||||
bind(&comps)
|
bind(&comps)
|
||||||
|
|
||||||
carapace.Gen(cmd).FlagCompletion(comps)
|
carapace.Gen(cmd).FlagCompletion(comps)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestrictTargets generates a cobra annotation map with a single console.CommandHiddenFilter key
|
// hideCommand generates a cobra annotation map with a single
|
||||||
// to a comma-separated list of filters to use in order to expose/hide commands based on requirements.
|
// console.CommandHiddenFilter key, which value is a comma-separated list
|
||||||
// Ex: cmd.Annotations = RestrictTargets("windows") will only show the command if the target is Windows.
|
// of filters to use in order to expose/hide commands based on requirements.
|
||||||
// Ex: cmd.Annotations = RestrictTargets("windows", "beacon") show the command if target is a beacon on Windows.
|
// Ex: cmd.Annotations = hideCommand("windows") will hide the cmd
|
||||||
func RestrictTargets(filters ...string) map[string]string {
|
// if the target session/beacon is not a Windows host.
|
||||||
|
func hideCommand(filters ...string) map[string]string {
|
||||||
if len(filters) == 0 {
|
if len(filters) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -74,62 +76,3 @@ func RestrictTargets(filters ...string) map[string]string {
|
|||||||
console.CommandFilterKey: filts,
|
console.CommandFilterKey: filts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeBind returns a commandBinder helper function
|
|
||||||
// @menu - The command menu to which the commands should be bound (either server or implant menu).
|
|
||||||
func makeBind(cmd *cobra.Command, con *client.SliverClient) func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) {
|
|
||||||
return func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) {
|
|
||||||
found := false
|
|
||||||
|
|
||||||
// Ensure the given command group is available in the menu.
|
|
||||||
if group != "" {
|
|
||||||
for _, grp := range cmd.Groups() {
|
|
||||||
if grp.Title == group {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
cmd.AddGroup(&cobra.Group{
|
|
||||||
ID: group,
|
|
||||||
Title: group,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind the command to the root
|
|
||||||
for _, command := range cmds {
|
|
||||||
cmd.AddCommand(command(con)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// commandBinder is a helper used to bind commands to a given menu, for a given "command help group".
|
|
||||||
//
|
|
||||||
// @group - Name of the group under which the command should be shown. Preferably use a string in the constants package.
|
|
||||||
// @ cmds - A list of functions returning a list of root commands to bind. See any package's `commands.go` file and function.
|
|
||||||
// type commandBinder func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command)
|
|
||||||
|
|
||||||
// [ Core ]
|
|
||||||
// [ Sessions ]
|
|
||||||
// [ Execution ]
|
|
||||||
// [ Filesystem ]
|
|
||||||
// [ Info ]
|
|
||||||
// [ Network (C2)]
|
|
||||||
// [ Network tools ]
|
|
||||||
// [ Payloads ]
|
|
||||||
// [ Privileges ]
|
|
||||||
// [ Processes ]
|
|
||||||
// [ Aliases ]
|
|
||||||
// [ Extensions ]
|
|
||||||
|
|
||||||
// Commands not to bind in CLI:
|
|
||||||
// - portforwarders
|
|
||||||
// - Socks (and wg-socks ?)
|
|
||||||
// - shell ?
|
|
||||||
|
|
||||||
// Take care of:
|
|
||||||
// - double bind help command
|
|
||||||
// - double bind session commands
|
|
||||||
// - don't bind readline command in CLI.
|
|
||||||
@@ -55,32 +55,3 @@ func ClientInterfacesCompleter() carapace.Action {
|
|||||||
return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':')
|
return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalProxyCompleter gives URL completion to all flags/arguments that accept a client proxy address.
|
|
||||||
func LocalProxyCompleter() carapace.Action {
|
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
|
||||||
prefix := ""
|
|
||||||
|
|
||||||
hostPort := carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action {
|
|
||||||
switch len(c.Parts) {
|
|
||||||
case 0:
|
|
||||||
return ClientInterfacesCompleter()
|
|
||||||
case 1:
|
|
||||||
return carapace.ActionMessage("server port")
|
|
||||||
default:
|
|
||||||
return carapace.ActionValues()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return carapace.ActionMultiParts("://", func(c carapace.Context) carapace.Action {
|
|
||||||
switch len(c.Parts) {
|
|
||||||
case 0:
|
|
||||||
return carapace.ActionValues("http", "https").Tag("proxy protocols").Suffix("://")
|
|
||||||
case 1:
|
|
||||||
return hostPort
|
|
||||||
default:
|
|
||||||
return carapace.ActionValues()
|
|
||||||
}
|
|
||||||
}).Invoke(c).Prefix(prefix).ToA()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -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,17 +25,18 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/util"
|
"github.com/bishopfox/sliver/util"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
|
||||||
"github.com/klauspost/compress/zstd"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CrackWordlistsCmd - Manage GPU cracking stations.
|
// CrackWordlistsCmd - Manage GPU cracking stations
|
||||||
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
|
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
@@ -54,8 +55,8 @@ func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackRulesCmd - Manage GPU cracking stations.
|
// CrackRulesCmd - Manage GPU cracking stations
|
||||||
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
|
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
@@ -74,8 +75,8 @@ func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackHcstat2Cmd - Manage GPU cracking stations.
|
// CrackHcstat2Cmd - Manage GPU cracking stations
|
||||||
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
@@ -94,7 +95,7 @@ func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverClient) {
|
func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
tw.AppendHeader(table.Row{"Name", "Size"})
|
tw.AppendHeader(table.Row{"Name", "Size"})
|
||||||
@@ -104,7 +105,7 @@ func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverClient)
|
|||||||
con.Printf("%s\n", tw.Render())
|
con.Printf("%s\n", tw.Render())
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverClient) {
|
func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
|
||||||
wordlistTable := table.NewWriter()
|
wordlistTable := table.NewWriter()
|
||||||
wordlistTable.SetTitle(console.Bold + "Wordlists" + console.Normal)
|
wordlistTable.SetTitle(console.Bold + "Wordlists" + console.Normal)
|
||||||
wordlistTable.SetStyle(settings.GetTableStyle(con))
|
wordlistTable.SetStyle(settings.GetTableStyle(con))
|
||||||
@@ -161,8 +162,8 @@ func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverC
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackWordlistsAddCmd - Manage GPU cracking stations.
|
// CrackWordlistsAddCmd - Manage GPU cracking stations
|
||||||
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
name, _ := cmd.Flags().GetString("name")
|
name, _ := cmd.Flags().GetString("name")
|
||||||
|
|
||||||
var localPath string
|
var localPath string
|
||||||
@@ -205,8 +206,8 @@ func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []
|
|||||||
addCrackFile(wordlist, crackFile, con)
|
addCrackFile(wordlist, crackFile, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackRulesAddCmd - add a rules file.
|
// CrackRulesAddCmd - add a rules file
|
||||||
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
name, _ := cmd.Flags().GetString("name")
|
name, _ := cmd.Flags().GetString("name")
|
||||||
|
|
||||||
var localPath string
|
var localPath string
|
||||||
@@ -249,8 +250,8 @@ func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
|||||||
addCrackFile(rules, crackFile, con)
|
addCrackFile(rules, crackFile, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackHcstat2AddCmd - add a hcstat2 file.
|
// CrackHcstat2AddCmd - add a hcstat2 file
|
||||||
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
name, _ := cmd.Flags().GetString("name")
|
name, _ := cmd.Flags().GetString("name")
|
||||||
|
|
||||||
var localPath string
|
var localPath string
|
||||||
@@ -293,7 +294,7 @@ func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
|||||||
addCrackFile(hcstat2, crackFile, con)
|
addCrackFile(hcstat2, crackFile, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverClient) {
|
func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverConsoleClient) {
|
||||||
digest := sha256.New()
|
digest := sha256.New()
|
||||||
wordlistReader := io.TeeReader(localFile, digest)
|
wordlistReader := io.TeeReader(localFile, digest)
|
||||||
|
|
||||||
@@ -401,8 +402,8 @@ func readChunkAt(tmpFile *os.File, offset int64, chunkSize int64) ([]byte, error
|
|||||||
return chunkBuf[:n], nil
|
return chunkBuf[:n], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackWordlistsRmCmd - Manage GPU cracking stations.
|
// CrackWordlistsRmCmd - Manage GPU cracking stations
|
||||||
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var wordlistName string
|
var wordlistName string
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
wordlistName = args[0]
|
wordlistName = args[0]
|
||||||
@@ -435,8 +436,8 @@ func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackRulesRmCmd - Manage GPU cracking stations.
|
// CrackRulesRmCmd - Manage GPU cracking stations
|
||||||
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var rulesName string
|
var rulesName string
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
rulesName = args[0]
|
rulesName = args[0]
|
||||||
@@ -469,8 +470,8 @@ func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CrackHcstat2RmCmd - remove a hcstat2 file.
|
// CrackHcstat2RmCmd - remove a hcstat2 file
|
||||||
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var hcstat2Name string
|
var hcstat2Name string
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
hcstat2Name = args[0]
|
hcstat2Name = args[0]
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CrackCmd - GPU password cracking interface
|
// CrackCmd - GPU password cracking interface
|
||||||
func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
if !AreCrackersOnline(con) {
|
if !AreCrackersOnline(con) {
|
||||||
PrintNoCrackstations(con)
|
PrintNoCrackstations(con)
|
||||||
} else {
|
} else {
|
||||||
@@ -58,7 +58,7 @@ func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CrackStationsCmd - Manage GPU cracking stations
|
// CrackStationsCmd - Manage GPU cracking stations
|
||||||
func CrackStationsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
@@ -71,11 +71,11 @@ func CrackStationsCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintNoCrackstations(con *console.SliverClient) {
|
func PrintNoCrackstations(con *console.SliverConsoleClient) {
|
||||||
con.PrintInfof("No crackstations connected to server\n")
|
con.PrintInfof("No crackstations connected to server\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func AreCrackersOnline(con *console.SliverClient) bool {
|
func AreCrackersOnline(con *console.SliverConsoleClient) bool {
|
||||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
@@ -83,7 +83,7 @@ func AreCrackersOnline(con *console.SliverClient) bool {
|
|||||||
return len(crackers.Crackstations) > 0
|
return len(crackers.Crackstations) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient) {
|
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverConsoleClient) {
|
||||||
sort.Slice(crackers, func(i, j int) bool {
|
sort.Slice(crackers, func(i, j int) bool {
|
||||||
return crackers[i].Name < crackers[j].Name
|
return crackers[i].Name < crackers[j].Name
|
||||||
})
|
})
|
||||||
@@ -96,7 +96,7 @@ func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverClient) {
|
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n")
|
tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n")
|
||||||
@@ -135,7 +135,7 @@ func printCracker(cracker *clientpb.Crackstation, index int, con *console.Sliver
|
|||||||
printBenchmarks(cracker, con)
|
printBenchmarks(cracker, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverClient) {
|
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
tw.SetTitle(console.Bold + "Benchmarks" + console.Normal)
|
tw.SetTitle(console.Bold + "Benchmarks" + console.Normal)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/rsteube/carapace"
|
"github.com/rsteube/carapace"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CrackHcstat2Completer(con *console.SliverClient) carapace.Action {
|
func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action {
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,7 +33,7 @@ func CrackHcstat2Completer(con *console.SliverClient) carapace.Action {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CrackWordlistCompleter(con *console.SliverClient) carapace.Action {
|
func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,7 +57,7 @@ func CrackWordlistCompleter(con *console.SliverClient) carapace.Action {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CrackRulesCompleter(con *console.SliverClient) carapace.Action {
|
func CrackRulesCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ const (
|
|||||||
CSVFormat = "csv" // username,hash\n
|
CSVFormat = "csv" // username,hash\n
|
||||||
)
|
)
|
||||||
|
|
||||||
// CredsCmd - Add new credentials.
|
// CredsCmd - Add new credentials
|
||||||
func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
collection, _ := cmd.Flags().GetString("collection")
|
collection, _ := cmd.Flags().GetString("collection")
|
||||||
username, _ := cmd.Flags().GetString("username")
|
username, _ := cmd.Flags().GetString("username")
|
||||||
plaintext, _ := cmd.Flags().GetString("plaintext")
|
plaintext, _ := cmd.Flags().GetString("plaintext")
|
||||||
@@ -76,8 +76,8 @@ func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
PrintCreds(creds.Credentials, con)
|
PrintCreds(creds.Credentials, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CredsCmd - Add new credentials.
|
// CredsCmd - Add new credentials
|
||||||
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
collection, _ := cmd.Flags().GetString("collection")
|
collection, _ := cmd.Flags().GetString("collection")
|
||||||
filePath := args[0]
|
filePath := args[0]
|
||||||
fileFormat, _ := cmd.Flags().GetString("file-format")
|
fileFormat, _ := cmd.Flags().GetString("file-format")
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -23,17 +23,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/rsteube/carapace"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
|
||||||
"github.com/rsteube/carapace"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CredsCmd - Manage credentials.
|
// CredsCmd - Manage credentials
|
||||||
func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
@@ -46,7 +47,7 @@ func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
PrintCreds(creds.Credentials, con)
|
PrintCreds(creds.Credentials, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintCreds(creds []*clientpb.Credential, con *console.SliverClient) {
|
func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient) {
|
||||||
collections := make(map[string][]*clientpb.Credential)
|
collections := make(map[string][]*clientpb.Credential)
|
||||||
for _, cred := range creds {
|
for _, cred := range creds {
|
||||||
collections[cred.Collection] = append(collections[cred.Collection], cred)
|
collections[cred.Collection] = append(collections[cred.Collection], cred)
|
||||||
@@ -57,7 +58,7 @@ func PrintCreds(creds []*clientpb.Credential, con *console.SliverClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverClient) {
|
func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
if collection != "" {
|
if collection != "" {
|
||||||
@@ -87,7 +88,7 @@ func printCollection(collection string, creds []*clientpb.Credential, con *conso
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CredsHashTypeCompleter completes hash types.
|
// CredsHashTypeCompleter completes hash types.
|
||||||
func CredsHashTypeCompleter(con *console.SliverClient) carapace.Action {
|
func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
results := make([]string, 0)
|
results := make([]string, 0)
|
||||||
|
|
||||||
@@ -100,8 +101,8 @@ func CredsHashTypeCompleter(con *console.SliverClient) carapace.Action {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CredsHashFileFormatCompleter completes file formats for hash-files.
|
// CredsHashFileFormatCompleter completes file formats for hash-files
|
||||||
func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action {
|
func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||||
return carapace.ActionValuesDescribed(
|
return carapace.ActionValuesDescribed(
|
||||||
UserColonHashNewlineFormat, "One hash per line.",
|
UserColonHashNewlineFormat, "One hash per line.",
|
||||||
HashNewlineFormat, "A file containing lines of 'username:hash' pairs.",
|
HashNewlineFormat, "A file containing lines of 'username:hash' pairs.",
|
||||||
@@ -109,8 +110,8 @@ func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action {
|
|||||||
).Tag("hash file formats")
|
).Tag("hash file formats")
|
||||||
}
|
}
|
||||||
|
|
||||||
// CredsCollectionCompleter completes existing creds collection names.
|
// CredsCollectionCompleter completes existing creds collection names
|
||||||
func CredsCollectionCompleter(con *console.SliverClient) carapace.Action {
|
func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
results := make([]string, 0)
|
results := make([]string, 0)
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ func CredsCollectionCompleter(con *console.SliverClient) carapace.Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CredsCredentialIDCompleter completes credential IDs.
|
// CredsCredentialIDCompleter completes credential IDs.
|
||||||
func CredsCredentialIDCompleter(con *console.SliverClient) carapace.Action {
|
func CredsCredentialIDCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
results := make([]string, 0)
|
results := make([]string, 0)
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CredsCmd - Add new credentials.
|
// CredsCmd - Add new credentials
|
||||||
func CredsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CredsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var id string
|
var id string
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
id = args[0]
|
id = args[0]
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SelectCredential - Interactive menu for the user to select a credentials from the database.
|
// SelectCredential - Interactive menu for the user to select a credentials from the database
|
||||||
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverClient) (*clientpb.Credential, error) {
|
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverConsoleClient) (*clientpb.Credential, error) {
|
||||||
var creds *clientpb.Credentials
|
var creds *clientpb.Credentials
|
||||||
var err error
|
var err error
|
||||||
if hashType == clientpb.HashType_INVALID {
|
if hashType == clientpb.HashType_INVALID {
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/core"
|
"github.com/bishopfox/sliver/client/core"
|
||||||
"github.com/bishopfox/sliver/client/overlord"
|
"github.com/bishopfox/sliver/client/overlord"
|
||||||
@@ -36,7 +38,6 @@ import (
|
|||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -50,8 +51,8 @@ var (
|
|||||||
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
|
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
|
||||||
)
|
)
|
||||||
|
|
||||||
// CursedChromeCmd - Execute a .NET assembly in-memory.
|
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||||
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
@@ -111,7 +112,7 @@ func CursedChromeCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess {
|
func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||||
chromeProcess, err := getChromeProcess(session, cmd, con)
|
chromeProcess, err := getChromeProcess(session, cmd, con)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
@@ -153,7 +154,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons
|
|||||||
return curse
|
return curse
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) {
|
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||||
name := "Chrome"
|
name := "Chrome"
|
||||||
if isEdge {
|
if isEdge {
|
||||||
name = "Edge"
|
name = "Edge"
|
||||||
@@ -253,7 +254,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra
|
|||||||
return curse, nil
|
return curse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) {
|
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||||
userDataFlag, _ := cmd.Flags().GetString("user-data")
|
userDataFlag, _ := cmd.Flags().GetString("user-data")
|
||||||
if userDataFlag != "" {
|
if userDataFlag != "" {
|
||||||
return userDataFlag, nil
|
return userDataFlag, nil
|
||||||
@@ -306,7 +307,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) {
|
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||||
exeFlag, _ := cmd.Flags().GetString("exe")
|
exeFlag, _ := cmd.Flags().GetString("exe")
|
||||||
if exeFlag != "" {
|
if exeFlag != "" {
|
||||||
return exeFlag, nil
|
return exeFlag, nil
|
||||||
@@ -416,7 +417,7 @@ func isChromeProcess(executable string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) {
|
func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,14 +27,15 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/reeflective/readline"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/core"
|
"github.com/bishopfox/sliver/client/core"
|
||||||
"github.com/bishopfox/sliver/client/overlord"
|
"github.com/bishopfox/sliver/client/overlord"
|
||||||
"github.com/reeflective/readline"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
curse := selectCursedProcess(con)
|
curse := selectCursedProcess(con)
|
||||||
if curse == nil {
|
if curse == nil {
|
||||||
return
|
return
|
||||||
@@ -55,7 +56,7 @@ func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
|||||||
startCursedConsole(curse, true, target, con)
|
startCursedConsole(curse, true, target, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverClient) *overlord.ChromeDebugTarget {
|
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget {
|
||||||
if len(targets) < 1 {
|
if len(targets) < 1 {
|
||||||
con.PrintErrorf("No debug targets\n")
|
con.PrintErrorf("No debug targets\n")
|
||||||
return nil
|
return nil
|
||||||
@@ -93,7 +94,7 @@ var helperHooks = []string{
|
|||||||
"console.log = (...a) => {return a;}", // console.log
|
"console.log = (...a) => {return a;}", // console.log
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverClient) {
|
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
|
||||||
tmpFile, _ := os.CreateTemp("", "cursed")
|
tmpFile, _ := os.CreateTemp("", "cursed")
|
||||||
shell := readline.NewShell()
|
shell := readline.NewShell()
|
||||||
shell.History.AddFromFile("cursed history", tmpFile.Name())
|
shell.History.AddFromFile("cursed history", tmpFile.Name())
|
||||||
|
|||||||
@@ -21,16 +21,16 @@ package cursed
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"encoding/json"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/overlord"
|
"github.com/bishopfox/sliver/client/overlord"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
curse := selectCursedProcess(con)
|
curse := selectCursedProcess(con)
|
||||||
if curse == nil {
|
if curse == nil {
|
||||||
return
|
return
|
||||||
@@ -47,24 +47,24 @@ func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
|||||||
if len(cookies) == 0 {
|
if len(cookies) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
saveFile, _ := cmd.Flags().GetString("save")
|
saveFile, _ := cmd.Flags().GetString("save")
|
||||||
if saveFile == "" {
|
if saveFile == "" {
|
||||||
saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405"))
|
saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405"))
|
||||||
}
|
}
|
||||||
|
jsonCookies := []string{}
|
||||||
jsonBytes, err := json.MarshalIndent(cookies, "", " ")
|
for _, cookie := range cookies {
|
||||||
if err != nil {
|
jsonCookie, err := cookie.MarshalJSON()
|
||||||
con.PrintErrorf("Failed to marshal cookies: %s\n", err)
|
if err != nil {
|
||||||
return
|
con.PrintErrorf("Failed to marshal cookie: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
jsonCookies = append(jsonCookies, string(jsonCookie))
|
||||||
}
|
}
|
||||||
|
err = os.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0o600)
|
||||||
err = os.WriteFile(saveFile, jsonBytes, 0o600)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("Failed to save cookies: %s\n", err)
|
con.PrintErrorf("Failed to save cookies: %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
con.PrintInfof("Saved to %s", saveFile)
|
con.PrintInfof("Saved to %s", saveFile)
|
||||||
con.Println()
|
con.Println()
|
||||||
}
|
}
|
||||||
@@ -21,20 +21,22 @@ package cursed
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/core"
|
"github.com/bishopfox/sliver/client/core"
|
||||||
"github.com/bishopfox/sliver/client/overlord"
|
"github.com/bishopfox/sliver/client/overlord"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CursedChromeCmd - Execute a .NET assembly in-memory.
|
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||||
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
@@ -94,7 +96,7 @@ func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess {
|
func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||||
edgeProcess, err := getEdgeProcess(session, cmd, con)
|
edgeProcess, err := getEdgeProcess(session, cmd, con)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s\n", err)
|
con.PrintErrorf("%s\n", err)
|
||||||
@@ -151,7 +153,7 @@ func isEdgeProcess(executable string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) {
|
func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/core"
|
"github.com/bishopfox/sliver/client/core"
|
||||||
"github.com/bishopfox/sliver/client/overlord"
|
"github.com/bishopfox/sliver/client/overlord"
|
||||||
@@ -34,10 +36,9 @@ import (
|
|||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CursedElectronCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
@@ -67,7 +68,7 @@ func CursedElectronCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
|||||||
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
|
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
|
||||||
}
|
}
|
||||||
|
|
||||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess {
|
func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||||
exists, err := checkElectronPath(electronExe, session, cmd, con)
|
exists, err := checkElectronPath(electronExe, session, cmd, con)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
con.PrintErrorf("%s", err)
|
con.PrintErrorf("%s", err)
|
||||||
@@ -118,7 +119,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co
|
|||||||
return curse
|
return curse
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (bool, error) {
|
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (bool, error) {
|
||||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
Path: electronExe,
|
Path: electronExe,
|
||||||
@@ -129,7 +130,7 @@ func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra
|
|||||||
return ls.GetExists(), nil
|
return ls.GetExists(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) {
|
func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
})
|
})
|
||||||
@@ -147,7 +148,7 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *co
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) {
|
func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||||
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
|
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
|
||||||
debugPort := getRemoteDebuggerPort(cmd)
|
debugPort := getRemoteDebuggerPort(cmd)
|
||||||
args := []string{
|
args := []string{
|
||||||
|
|||||||
@@ -23,13 +23,14 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/core"
|
"github.com/bishopfox/sliver/client/core"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CursedRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,12 +23,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/overlord"
|
"github.com/bishopfox/sliver/client/overlord"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
curse := selectCursedProcess(con)
|
curse := selectCursedProcess(con)
|
||||||
if curse == nil {
|
if curse == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -27,15 +27,16 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/core"
|
"github.com/bishopfox/sliver/client/core"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CursedChromeCmd - Execute a .NET assembly in-memory.
|
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||||
func CursedCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
// Collect existing curses from core
|
// Collect existing curses from core
|
||||||
cursedProcesses := [][]string{}
|
cursedProcesses := [][]string{}
|
||||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||||
@@ -70,8 +71,8 @@ func CursedCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectCursedProcess - Interactively select a cursed process from a list.
|
// selectCursedProcess - Interactively select a cursed process from a list
|
||||||
func selectCursedProcess(con *console.SliverClient) *core.CursedProcess {
|
func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
|
||||||
cursedProcesses := []*core.CursedProcess{}
|
cursedProcesses := []*core.CursedProcess{}
|
||||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||||
cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess))
|
cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess))
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -34,7 +34,7 @@ import (
|
|||||||
// dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH
|
// dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH
|
||||||
|
|
||||||
// DllHijackCmd -- implements the dllhijack command
|
// DllHijackCmd -- implements the dllhijack command
|
||||||
func DllHijackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var (
|
var (
|
||||||
localRefData []byte
|
localRefData []byte
|
||||||
targetDLLData []byte
|
targetDLLData []byte
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// EnvGetCmd - Get a remote environment variable
|
// EnvGetCmd - Get a remote environment variable
|
||||||
func EnvGetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -66,7 +66,7 @@ func EnvGetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintGetEnvInfo - Print the results of the env get command
|
// PrintGetEnvInfo - Print the results of the env get command
|
||||||
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverClient) {
|
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
|
||||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// EnvSetCmd - Set a remote environment variable
|
// EnvSetCmd - Set a remote environment variable
|
||||||
func EnvSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -72,7 +72,7 @@ func EnvSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintSetEnvInfo - Print the set environment info
|
// PrintSetEnvInfo - Print the set environment info
|
||||||
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverClient) {
|
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
|
||||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// EnvUnsetCmd - Unset a remote environment variable
|
// EnvUnsetCmd - Unset a remote environment variable
|
||||||
func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -67,7 +67,7 @@ func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintUnsetEnvInfo - Print the set environment info
|
// PrintUnsetEnvInfo - Print the set environment info
|
||||||
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverClient) {
|
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) {
|
||||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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}
|
|
||||||
}
|
|
||||||
@@ -25,15 +25,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory.
|
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
|
||||||
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -95,7 +96,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
|||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
IsDLL: isDLL,
|
IsDLL: isDLL,
|
||||||
Process: process,
|
Process: process,
|
||||||
Arguments: assemblyArgs,
|
Arguments: assemblyArgsStr,
|
||||||
Assembly: assemblyBytes,
|
Assembly: assemblyBytes,
|
||||||
Arch: arch,
|
Arch: arch,
|
||||||
Method: method,
|
Method: method,
|
||||||
@@ -131,7 +132,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||||
lootName, _ := cmd.Flags().GetString("name")
|
lootName, _ := cmd.Flags().GetString("name")
|
||||||
|
|
||||||
|
|||||||
@@ -26,17 +26,19 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/term"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/core"
|
"github.com/bishopfox/sliver/client/core"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"golang.org/x/term"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecuteShellcodeCmd - Execute shellcode in-memory.
|
// ExecuteShellcodeCmd - Execute shellcode in-memory
|
||||||
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -126,8 +128,8 @@ func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintExecuteShellcode - Display result of shellcode execution.
|
// PrintExecuteShellcode - Display result of shellcode execution
|
||||||
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverClient) {
|
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||||
if task.Response.GetErr() != "" {
|
if task.Response.GetErr() != "" {
|
||||||
con.PrintErrorf("%s\n", task.Response.GetErr())
|
con.PrintErrorf("%s\n", task.Response.GetErr())
|
||||||
} else {
|
} else {
|
||||||
@@ -135,7 +137,7 @@ func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverClient) {
|
func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||||
// Check active session
|
// Check active session
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
|
|||||||
@@ -23,16 +23,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/loot"
|
"github.com/bishopfox/sliver/client/command/loot"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecuteCmd - Run a command on the remote system.
|
// ExecuteCmd - Run a command on the remote system
|
||||||
func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -64,7 +66,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||||
if token || hidden || ppid != 0 {
|
if token || hidden || ppid != 0 {
|
||||||
if (session != nil && session.OS != "windows") || (beacon != nil && beacon.OS != "windows") {
|
if session.OS != "windows" {
|
||||||
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
|
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -111,7 +113,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
var lootedOutput []byte
|
var lootedOutput []byte
|
||||||
stdout, _ := cmd.Flags().GetString("stdout")
|
stdout, _ := cmd.Flags().GetString("stdout")
|
||||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||||
@@ -134,8 +136,8 @@ func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName stri
|
|||||||
PrintExecute(exec, cmd, con)
|
PrintExecute(exec, cmd, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintExecute - Print the output of an executed command.
|
// PrintExecute - Print the output of an executed command
|
||||||
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverClient) {
|
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
|
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
|
||||||
stdout, _ := cmd.Flags().GetString("stdout")
|
stdout, _ := cmd.Flags().GetString("stdout")
|
||||||
stderr, _ := cmd.Flags().GetString("stderr")
|
stderr, _ := cmd.Flags().GetString("stderr")
|
||||||
@@ -208,7 +210,7 @@ func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStd
|
|||||||
return []byte(outputString)
|
return []byte(outputString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverClient) {
|
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverConsoleClient) {
|
||||||
if len(commandOutput) == 0 {
|
if len(commandOutput) == 0 {
|
||||||
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
|
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
|
||||||
return
|
return
|
||||||
@@ -227,7 +229,7 @@ func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cm
|
|||||||
loot.SendLootMessage(lootMessage, con)
|
loot.SendLootMessage(lootMessage, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverClient) {
|
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverConsoleClient) {
|
||||||
con.PrintInfof("Output:\n%s", executionOutput)
|
con.PrintInfof("Output:\n%s", executionOutput)
|
||||||
|
|
||||||
if saveOutput {
|
if saveOutput {
|
||||||
@@ -235,7 +237,7 @@ func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverClient) {
|
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverConsoleClient) {
|
||||||
var outFilePath *os.File
|
var outFilePath *os.File
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -256,7 +258,7 @@ func SaveExecutionOutput(executionOutput string, commandName string, hostName st
|
|||||||
}
|
}
|
||||||
|
|
||||||
if outFilePath != nil {
|
if outFilePath != nil {
|
||||||
outFilePath.WriteString(executionOutput)
|
outFilePath.Write([]byte(executionOutput))
|
||||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,19 +21,19 @@ package exec
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
consts "github.com/bishopfox/sliver/client/constants"
|
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MigrateCmd - Windows only, inject an implant into another process
|
// MigrateCmd - Windows only, inject an implant into another process
|
||||||
func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session := con.ActiveTarget.GetSession()
|
||||||
if session == nil && beacon == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,47 +43,44 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
con.PrintErrorf("Error: Must specify either a PID or process name\n")
|
con.PrintErrorf("Error: Must specify either a PID or process name\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if procName != "" {
|
||||||
var config *clientpb.ImplantConfig
|
procCtrl := make(chan bool)
|
||||||
var implantName string
|
con.SpinUntil(fmt.Sprintf("Searching for %s ...", procName), procCtrl)
|
||||||
|
proc, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||||
if session != nil {
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
config = con.GetActiveSessionConfig()
|
})
|
||||||
implantName = session.Name
|
if err != nil {
|
||||||
} else {
|
con.PrintErrorf("Error: %v\n", err)
|
||||||
config = con.GetActiveBeaconConfig()
|
return
|
||||||
implantName = beacon.Name
|
}
|
||||||
|
procCtrl <- true
|
||||||
|
<-procCtrl
|
||||||
|
for _, p := range proc.GetProcesses() {
|
||||||
|
if strings.EqualFold(p.Executable, procName) {
|
||||||
|
pid = uint32(p.Pid)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pid == 0 {
|
||||||
|
con.PrintErrorf("Error: Could not find process %s\n", procName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
con.PrintInfof("Process name specified, overriding PID with %d\n", pid)
|
||||||
}
|
}
|
||||||
|
config := con.GetActiveSessionConfig()
|
||||||
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
|
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
|
||||||
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
|
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
|
||||||
encoder = clientpb.ShellcodeEncoder_NONE
|
encoder = clientpb.ShellcodeEncoder_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
if pid != 0 {
|
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
||||||
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
|
||||||
} else {
|
|
||||||
con.SpinUntil(fmt.Sprintf("Migrating into %s...", procName), ctrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the HTTP C2 Config name is not defined, then put in the default value
|
|
||||||
This will have no effect on implants that do not use HTTP C2
|
|
||||||
Also this should be overridden when the build info is pulled from the
|
|
||||||
database, but if there is no build info and we have to create the build
|
|
||||||
from scratch, we need to have something in here.
|
|
||||||
*/
|
|
||||||
if config.HTTPC2ConfigName == "" {
|
|
||||||
config.HTTPC2ConfigName = consts.DefaultC2Profile
|
|
||||||
}
|
|
||||||
|
|
||||||
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
||||||
Pid: pid,
|
Pid: pid,
|
||||||
Config: config,
|
Config: config,
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
Encoder: encoder,
|
Encoder: encoder,
|
||||||
Name: implantName,
|
|
||||||
ProcName: procName,
|
|
||||||
})
|
})
|
||||||
ctrl <- true
|
ctrl <- true
|
||||||
<-ctrl
|
<-ctrl
|
||||||
@@ -91,32 +88,9 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
con.PrintErrorf("Error: %v", err)
|
con.PrintErrorf("Error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if migrate.Response != nil && migrate.Response.Async {
|
if !migrate.Success {
|
||||||
con.AddBeaconCallback(migrate.Response.TaskID, func(task *clientpb.BeaconTask) {
|
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
|
||||||
err = proto.Unmarshal(task.Response, migrate)
|
return
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
|
||||||
}
|
|
||||||
if !migrate.Success {
|
|
||||||
if migrate.GetResponse().GetErr() != "" {
|
|
||||||
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
|
|
||||||
} else {
|
|
||||||
con.PrintErrorf("Could not migrate into a new process. Check the PID or name.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
con.PrintInfof("Successfully migrated to %d\n", migrate.Pid)
|
|
||||||
})
|
|
||||||
con.PrintAsyncResponse(migrate.Response)
|
|
||||||
} else {
|
|
||||||
if !migrate.Success {
|
|
||||||
if migrate.GetResponse().GetErr() != "" {
|
|
||||||
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
|
|
||||||
} else {
|
|
||||||
con.PrintErrorf("Could not migrate into a new process. Check the PID or name.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
con.PrintInfof("Successfully migrated to %d\n", migrate.Pid)
|
|
||||||
}
|
}
|
||||||
|
con.PrintInfof("Successfully migrated to %d\n", pid)
|
||||||
}
|
}
|
||||||
@@ -22,16 +22,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
consts "github.com/bishopfox/sliver/client/constants"
|
consts "github.com/bishopfox/sliver/client/constants"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsfInjectCmd - Inject a metasploit payload into a remote process.
|
// MsfInjectCmd - Inject a metasploit payload into a remote process
|
||||||
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -49,7 +51,7 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if pid == -1 {
|
if pid == -1 {
|
||||||
con.PrintErrorf("Invalid pid '%d', see `help %s`\n", pid, consts.MsfInjectStr)
|
con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var goos string
|
var goos string
|
||||||
@@ -97,8 +99,8 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintMsfRemote - Print the results of the remote injection attempt.
|
// PrintMsfRemote - Print the results of the remote injection attempt
|
||||||
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverClient) {
|
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||||
if msfRemote.Response == nil {
|
if msfRemote.Response == nil {
|
||||||
con.PrintErrorf("Empty response from msf payload injection task")
|
con.PrintErrorf("Empty response from msf payload injection task")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -22,15 +22,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
consts "github.com/bishopfox/sliver/client/constants"
|
consts "github.com/bishopfox/sliver/client/constants"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsfCmd - Inject a metasploit payload into the current remote process.
|
// MsfCmd - Inject a metasploit payload into the current remote process
|
||||||
func MsfCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ package exec
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
insecureRand "math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
insecureRand "math/rand"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/generate"
|
"github.com/bishopfox/sliver/client/command/generate"
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
@@ -33,11 +36,10 @@ import (
|
|||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/bishopfox/sliver/util/encoders"
|
"github.com/bishopfox/sliver/util/encoders"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PsExecCmd - psexec command implementation.
|
// PsExecCmd - psexec command implementation.
|
||||||
func PsExecCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -25,30 +25,24 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SideloadCmd - Sideload a shared library on the remote system.
|
// SideloadCmd - Sideload a shared library on the remote system
|
||||||
func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
cmd.Usage()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
binPath := args[0]
|
binPath := args[0]
|
||||||
var binArgs []string
|
binArgs := strings.Join(args[1:], " ")
|
||||||
if len(args) > 1 {
|
|
||||||
binArgs = args[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
entryPoint, _ := cmd.Flags().GetString("entry-point")
|
entryPoint, _ := cmd.Flags().GetString("entry-point")
|
||||||
processName, _ := cmd.Flags().GetString("process")
|
processName, _ := cmd.Flags().GetString("process")
|
||||||
@@ -65,7 +59,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
processArgs := strings.Split(processArgsStr, " ")
|
processArgs := strings.Split(processArgsStr, " ")
|
||||||
isDLL := (filepath.Ext(binPath) == ".dll")
|
isDLL := (filepath.Ext(binPath) == ".dll")
|
||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
con.SpinUntil(fmt.Sprintf("Sideloading %s %v...", binPath, binArgs), ctrl)
|
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||||
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
Args: binArgs,
|
Args: binArgs,
|
||||||
@@ -102,7 +96,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||||
lootName, _ := cmd.Flags().GetString("name")
|
lootName, _ := cmd.Flags().GetString("name")
|
||||||
|
|
||||||
|
|||||||
@@ -22,25 +22,25 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SpawnDllCmd - Spawn execution of a DLL on the remote system.
|
// SpawnDllCmd - Spawn execution of a DLL on the remote system
|
||||||
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
binPath := args[0]
|
binPath := args[0]
|
||||||
var dllArgs []string
|
dllArgs := strings.Join(args[1:], " ")
|
||||||
if len(args) > 1 {
|
|
||||||
dllArgs = args[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
processName, _ := cmd.Flags().GetString("process")
|
processName, _ := cmd.Flags().GetString("process")
|
||||||
exportName, _ := cmd.Flags().GetString("export")
|
exportName, _ := cmd.Flags().GetString("export")
|
||||||
@@ -85,7 +85,7 @@ func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||||
lootName, _ := cmd.Flags().GetString("name")
|
lootName, _ := cmd.Flags().GetString("name")
|
||||||
|
|
||||||
|
|||||||
@@ -24,15 +24,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out).
|
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
|
||||||
func SSHCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var (
|
var (
|
||||||
privKey []byte
|
privKey []byte
|
||||||
err error
|
err error
|
||||||
@@ -119,8 +120,8 @@ func SSHCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintSSHCmd - Print the ssh command response.
|
// PrintSSHCmd - Print the ssh command response
|
||||||
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverClient) {
|
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
|
||||||
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
|
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
|
||||||
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
|
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
|
||||||
if sshCmd.StdErr != "" {
|
if sshCmd.StdErr != "" {
|
||||||
@@ -138,7 +139,7 @@ func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryCredsFromLoot(con *console.SliverClient) (string, string, []byte) {
|
func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte) {
|
||||||
var (
|
var (
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
|
|||||||
@@ -25,13 +25,12 @@ import (
|
|||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/client/constants"
|
|
||||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExitCmd - Exit the console.
|
// ExitCmd - Exit the console
|
||||||
func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
fmt.Println("Exiting...")
|
fmt.Println("Exiting...")
|
||||||
if con.IsServer {
|
if con.IsServer {
|
||||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||||
@@ -54,15 +53,3 @@ func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commands returns the `exit` command.
|
|
||||||
func Command(con *console.SliverClient) []*cobra.Command {
|
|
||||||
return []*cobra.Command{{
|
|
||||||
Use: "exit",
|
|
||||||
Short: "Exit the program",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
ExitCmd(cmd, con, args)
|
|
||||||
},
|
|
||||||
GroupID: constants.GenericHelpGroup,
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
@@ -1,413 +0,0 @@
|
|||||||
package extensions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/core"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseFlagArgumentsToBuffer parses flag-style arguments based on extension manifest
|
|
||||||
// and converts them to a BOF-compatible binary buffer
|
|
||||||
func ParseFlagArgumentsToBuffer(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) {
|
|
||||||
// Create a flag set and parse the arguments
|
|
||||||
fs, stringValues, wstringValues, intValues, shortValues, fileValues, err := bofSetupAndParseFlags(args, ext)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print debug information about parsed flags
|
|
||||||
//bofDebugPrintParsedFlags(fs, ext, stringValues, wstringValues, intValues, shortValues, fileValues)
|
|
||||||
|
|
||||||
// Initialize the BOF arguments buffer
|
|
||||||
argsBuffer := core.BOFArgsBuffer{
|
|
||||||
Buffer: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process arguments and build the buffer
|
|
||||||
missingRequiredArgs, err := bofProcessArgumentsToBuffer(fs, ext, argsBuffer,
|
|
||||||
stringValues, wstringValues, intValues, shortValues, fileValues)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return error if we have missing required arguments
|
|
||||||
if len(missingRequiredArgs) > 0 {
|
|
||||||
return nil, fmt.Errorf("required arguments %s were not provided", strings.Join(missingRequiredArgs, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the final binary buffer
|
|
||||||
parsedArgs, err := argsBuffer.GetBuffer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get buffer: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedArgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofSetupAndParseFlags creates a flag set, defines expected flags based on the extension manifest,
|
|
||||||
// and parses the provided arguments
|
|
||||||
func bofSetupAndParseFlags(args []string, ext *ExtCommand) (*flag.FlagSet,
|
|
||||||
map[string]*string, map[string]*string, map[string]*int, map[string]*int, map[string]*string, error) {
|
|
||||||
|
|
||||||
// Create a new FlagSet
|
|
||||||
fs := flag.NewFlagSet("sliver-bof", flag.ContinueOnError)
|
|
||||||
|
|
||||||
// Maps to store flag value pointers
|
|
||||||
stringValues := make(map[string]*string)
|
|
||||||
intValues := make(map[string]*int)
|
|
||||||
shortValues := make(map[string]*int)
|
|
||||||
fileValues := make(map[string]*string) // Path to file data
|
|
||||||
wstringValues := make(map[string]*string)
|
|
||||||
|
|
||||||
// Define expected flags based on manifest arguments
|
|
||||||
for _, arg := range ext.Arguments {
|
|
||||||
flagName := arg.Name
|
|
||||||
flagDesc := arg.Desc
|
|
||||||
|
|
||||||
switch arg.Type {
|
|
||||||
case "string":
|
|
||||||
stringValues[flagName] = fs.String(flagName, "", flagDesc)
|
|
||||||
case "wstring":
|
|
||||||
wstringValues[flagName] = fs.String(flagName, "", flagDesc)
|
|
||||||
case "int", "integer":
|
|
||||||
intValues[flagName] = fs.Int(flagName, 0, flagDesc)
|
|
||||||
case "short":
|
|
||||||
shortValues[flagName] = fs.Int(flagName, 0, flagDesc)
|
|
||||||
case "file":
|
|
||||||
fileValues[flagName] = fs.String(flagName, "", flagDesc)
|
|
||||||
default:
|
|
||||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("unsupported argument type: %s", arg.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the arguments
|
|
||||||
if err := fs.Parse(args); err != nil {
|
|
||||||
return nil, nil, nil, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return fs, stringValues, wstringValues, intValues, shortValues, fileValues, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofProcessArgumentsToBuffer processes each argument and adds its value to the buffer
|
|
||||||
// Returns a list of missing required arguments and any error encountered
|
|
||||||
func bofProcessArgumentsToBuffer(fs *flag.FlagSet, ext *ExtCommand, argsBuffer core.BOFArgsBuffer,
|
|
||||||
stringValues, wstringValues map[string]*string,
|
|
||||||
intValues, shortValues map[string]*int,
|
|
||||||
fileValues map[string]*string) ([]string, error) {
|
|
||||||
|
|
||||||
missingRequiredArgs := make([]string, 0)
|
|
||||||
|
|
||||||
for _, argDef := range ext.Arguments {
|
|
||||||
var provided bool
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Process based on argument type
|
|
||||||
switch argDef.Type {
|
|
||||||
case "string":
|
|
||||||
provided, err = bofProcessStringArg(fs, argDef, stringValues, argsBuffer)
|
|
||||||
case "wstring":
|
|
||||||
provided, err = bofProcessWStringArg(fs, argDef, wstringValues, argsBuffer)
|
|
||||||
case "int", "integer":
|
|
||||||
provided, err = bofProcessIntArg(fs, argDef, intValues, argsBuffer)
|
|
||||||
case "short":
|
|
||||||
provided, err = bofProcessShortArg(fs, argDef, shortValues, argsBuffer)
|
|
||||||
case "file":
|
|
||||||
provided, err = bofProcessFileArg(fs, argDef, fileValues, argsBuffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If argument not provided, handle according to rules
|
|
||||||
if !provided {
|
|
||||||
if argDef.Optional {
|
|
||||||
// Try to apply default or type-appropriate zero value
|
|
||||||
err = bofApplyDefaultValue(argDef, argsBuffer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Required argument was not provided
|
|
||||||
missingRequiredArgs = append(missingRequiredArgs, "`"+argDef.Name+"`")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return missingRequiredArgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofProcessStringArg processes a string argument
|
|
||||||
func bofProcessStringArg(fs *flag.FlagSet, argDef *extensionArgument, stringValues map[string]*string,
|
|
||||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
|
||||||
|
|
||||||
ptr := stringValues[argDef.Name]
|
|
||||||
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
|
|
||||||
|
|
||||||
if flagWasSet {
|
|
||||||
err := argsBuffer.AddString(*ptr)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to add string argument %s: %v", argDef.Name, err)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofProcessWStringArg processes a wide string argument
|
|
||||||
func bofProcessWStringArg(fs *flag.FlagSet, argDef *extensionArgument, wstringValues map[string]*string,
|
|
||||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
|
||||||
|
|
||||||
ptr := wstringValues[argDef.Name]
|
|
||||||
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
|
|
||||||
|
|
||||||
if flagWasSet {
|
|
||||||
err := argsBuffer.AddWString(*ptr)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to add wstring argument %s: %v", argDef.Name, err)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofProcessIntArg processes an integer argument
|
|
||||||
func bofProcessIntArg(fs *flag.FlagSet, argDef *extensionArgument, intValues map[string]*int,
|
|
||||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
|
||||||
|
|
||||||
ptr := intValues[argDef.Name]
|
|
||||||
flagWasSet := bofFlagWasProvided(fs, argDef.Name)
|
|
||||||
|
|
||||||
if flagWasSet {
|
|
||||||
err := argsBuffer.AddInt(uint32(*ptr))
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to add int argument %s: %v", argDef.Name, err)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofProcessShortArg processes a short integer argument
|
|
||||||
func bofProcessShortArg(fs *flag.FlagSet, argDef *extensionArgument, shortValues map[string]*int,
|
|
||||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
|
||||||
|
|
||||||
ptr := shortValues[argDef.Name]
|
|
||||||
flagWasSet := bofFlagWasProvided(fs, argDef.Name)
|
|
||||||
|
|
||||||
if flagWasSet {
|
|
||||||
err := argsBuffer.AddShort(uint16(*ptr))
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to add short argument %s: %v", argDef.Name, err)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofProcessFileArg processes a file argument
|
|
||||||
func bofProcessFileArg(fs *flag.FlagSet, argDef *extensionArgument, fileValues map[string]*string,
|
|
||||||
argsBuffer core.BOFArgsBuffer) (bool, error) {
|
|
||||||
|
|
||||||
ptr := fileValues[argDef.Name]
|
|
||||||
flagWasSet := *ptr != "" && bofFlagWasProvided(fs, argDef.Name)
|
|
||||||
|
|
||||||
if flagWasSet {
|
|
||||||
// Validate file exists and read it
|
|
||||||
data, err := os.ReadFile(*ptr)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("error reading file for argument %s: %v", argDef.Name, err)
|
|
||||||
}
|
|
||||||
err = argsBuffer.AddData(data)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("failed to add file data for argument %s: %v", argDef.Name, err)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofApplyDefaultValue applies a default value based on the argument definition
|
|
||||||
func bofApplyDefaultValue(argDef *extensionArgument, argsBuffer core.BOFArgsBuffer) error {
|
|
||||||
// Try to use default value from manifest if available
|
|
||||||
if argDef.Default != nil {
|
|
||||||
switch argDef.Type {
|
|
||||||
case "string":
|
|
||||||
// Handle string default - could be string literal or numeric in JSON
|
|
||||||
defaultVal, ok := argDef.Default.(string)
|
|
||||||
if !ok {
|
|
||||||
// Try to convert from other types
|
|
||||||
defaultVal = fmt.Sprintf("%v", argDef.Default)
|
|
||||||
}
|
|
||||||
err := argsBuffer.AddString(defaultVal)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default string: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:%s (default)\n", argDef.Name, defaultVal)
|
|
||||||
|
|
||||||
case "wstring":
|
|
||||||
// Handle wstring default
|
|
||||||
defaultVal, ok := argDef.Default.(string)
|
|
||||||
if !ok {
|
|
||||||
defaultVal = fmt.Sprintf("%v", argDef.Default)
|
|
||||||
}
|
|
||||||
err := argsBuffer.AddWString(defaultVal)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default wstring: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:%s (default)\n", argDef.Name, defaultVal)
|
|
||||||
|
|
||||||
case "int", "integer":
|
|
||||||
// Handle int default - could be number or string in JSON
|
|
||||||
var defaultInt uint32
|
|
||||||
|
|
||||||
// Try as number first
|
|
||||||
numVal, ok := argDef.Default.(float64) // JSON unmarshals numbers as float64
|
|
||||||
if ok {
|
|
||||||
defaultInt = uint32(numVal)
|
|
||||||
} else {
|
|
||||||
// Try as string
|
|
||||||
strVal, ok := argDef.Default.(string)
|
|
||||||
if ok {
|
|
||||||
val, err := strconv.ParseUint(strVal, 10, 32)
|
|
||||||
if err == nil {
|
|
||||||
defaultInt = uint32(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := argsBuffer.AddInt(defaultInt)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default int: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:%d (default)\n", argDef.Name, defaultInt)
|
|
||||||
|
|
||||||
case "short":
|
|
||||||
// Handle short default - could be number or string in JSON
|
|
||||||
var defaultShort uint16
|
|
||||||
|
|
||||||
// Try as number first
|
|
||||||
numVal, ok := argDef.Default.(float64) // JSON unmarshals numbers as float64
|
|
||||||
if ok {
|
|
||||||
defaultShort = uint16(numVal)
|
|
||||||
} else {
|
|
||||||
// Try as string
|
|
||||||
strVal, ok := argDef.Default.(string)
|
|
||||||
if ok {
|
|
||||||
val, err := strconv.ParseUint(strVal, 10, 16)
|
|
||||||
if err == nil {
|
|
||||||
defaultShort = uint16(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := argsBuffer.AddShort(defaultShort)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default short: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:%d (default)\n", argDef.Name, defaultShort)
|
|
||||||
|
|
||||||
case "file":
|
|
||||||
// Default for file doesn't really make sense, but handle for completeness
|
|
||||||
err := argsBuffer.AddData([]byte{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default file data: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No default specified in manifest, use type-appropriate zero values
|
|
||||||
switch argDef.Type {
|
|
||||||
case "string":
|
|
||||||
err := argsBuffer.AddString("")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default string: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
|
||||||
|
|
||||||
case "wstring":
|
|
||||||
err := argsBuffer.AddWString("")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default wstring: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
|
||||||
|
|
||||||
case "int", "integer":
|
|
||||||
err := argsBuffer.AddInt(0)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default int: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:0 (default)\n", argDef.Name)
|
|
||||||
|
|
||||||
case "short":
|
|
||||||
err := argsBuffer.AddShort(0)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default short: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:0 (default)\n", argDef.Name)
|
|
||||||
|
|
||||||
case "file":
|
|
||||||
// Empty data for optional file
|
|
||||||
err := argsBuffer.AddData([]byte{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add default file data: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" -%s:<empty> (default)\n", argDef.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofDebugPrintParsedFlags prints information about the parsed flags
|
|
||||||
func bofDebugPrintParsedFlags(fs *flag.FlagSet, ext *ExtCommand,
|
|
||||||
stringValues map[string]*string,
|
|
||||||
wstringValues map[string]*string,
|
|
||||||
intValues map[string]*int,
|
|
||||||
shortValues map[string]*int,
|
|
||||||
fileValues map[string]*string) {
|
|
||||||
|
|
||||||
fmt.Println("parsed flags:")
|
|
||||||
|
|
||||||
for _, arg := range ext.Arguments {
|
|
||||||
flagName := arg.Name
|
|
||||||
wasProvided := bofFlagWasProvided(fs, flagName)
|
|
||||||
|
|
||||||
if wasProvided {
|
|
||||||
switch arg.Type {
|
|
||||||
case "string":
|
|
||||||
fmt.Printf(" -%s:%s\n", flagName, *stringValues[flagName])
|
|
||||||
case "wstring":
|
|
||||||
fmt.Printf(" -%s:%s\n", flagName, *wstringValues[flagName])
|
|
||||||
case "int", "integer":
|
|
||||||
fmt.Printf(" -%s:%d\n", flagName, *intValues[flagName])
|
|
||||||
case "short":
|
|
||||||
fmt.Printf(" -%s:%d\n", flagName, *shortValues[flagName])
|
|
||||||
case "file":
|
|
||||||
fmt.Printf(" -%s:%s\n", flagName, *fileValues[flagName])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bofFlagWasProvided checks if a flag was explicitly set
|
|
||||||
func bofFlagWasProvided(fs *flag.FlagSet, name string) bool {
|
|
||||||
provided := false
|
|
||||||
fs.Visit(func(f *flag.Flag) {
|
|
||||||
if f.Name == name {
|
|
||||||
provided = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return provided
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package extensions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/bishopfox/sliver/client/command/flags"
|
|
||||||
"github.com/bishopfox/sliver/client/command/help"
|
|
||||||
"github.com/bishopfox/sliver/client/command/use"
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
|
||||||
consts "github.com/bishopfox/sliver/client/constants"
|
|
||||||
"github.com/rsteube/carapace"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Commands returns the 'extensions' command and its subcommands.
|
|
||||||
func Commands(con *console.SliverClient) []*cobra.Command {
|
|
||||||
extensionCmd := &cobra.Command{
|
|
||||||
Use: consts.ExtensionsStr,
|
|
||||||
Short: "Manage extensions",
|
|
||||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr}),
|
|
||||||
GroupID: consts.GenericHelpGroup,
|
|
||||||
Run: func(cmd *cobra.Command, _ []string) {
|
|
||||||
ExtensionsCmd(cmd, con)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
extensionLoadCmd := &cobra.Command{
|
|
||||||
Use: consts.LoadStr,
|
|
||||||
Short: "Temporarily load an extension from a local directory",
|
|
||||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.LoadStr}),
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
ExtensionLoadCmd(cmd, con, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
extensionCmd.AddCommand(extensionLoadCmd)
|
|
||||||
carapace.Gen(extensionLoadCmd).PositionalCompletion(carapace.ActionDirectories().Usage("path to the extension directory"))
|
|
||||||
|
|
||||||
extensionInstallCmd := &cobra.Command{
|
|
||||||
Use: consts.InstallStr,
|
|
||||||
Short: "Install an extension from a local directory or .tar.gz file",
|
|
||||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.InstallStr}),
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
ExtensionsInstallCmd(cmd, con, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
extensionCmd.AddCommand(extensionInstallCmd)
|
|
||||||
carapace.Gen(extensionInstallCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to the extension .tar.gz or directory"))
|
|
||||||
|
|
||||||
extensionRmCmd := &cobra.Command{
|
|
||||||
Use: consts.RmStr,
|
|
||||||
Short: "Remove an installed extension",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.RmStr}),
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
ExtensionsRemoveCmd(cmd, con, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
extensionCmd.AddCommand(extensionRmCmd)
|
|
||||||
carapace.Gen(extensionRmCmd).PositionalCompletion(ExtensionsCommandNameCompleter(con).Usage("the command name of the extension to remove"))
|
|
||||||
|
|
||||||
return []*cobra.Command{extensionCmd}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliverCommands(con *console.SliverClient) []*cobra.Command {
|
|
||||||
extensionCmd := &cobra.Command{
|
|
||||||
Use: consts.ExtensionsStr,
|
|
||||||
Short: "Manage extensions",
|
|
||||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr}),
|
|
||||||
GroupID: consts.InfoHelpGroup,
|
|
||||||
Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), // restrict to session targets since we cannot `list` "loaded" extensions from beacon mode
|
|
||||||
}
|
|
||||||
|
|
||||||
listCmd := &cobra.Command{
|
|
||||||
Use: consts.ListStr,
|
|
||||||
Short: "List extensions loaded in the current session",
|
|
||||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.ListStr}),
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
ExtensionsListCmd(cmd, con, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
flags.Bind("use", false, listCmd, func(f *pflag.FlagSet) {
|
|
||||||
f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
|
|
||||||
})
|
|
||||||
extensionCmd.AddCommand(listCmd)
|
|
||||||
|
|
||||||
carapace.Gen(listCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con))
|
|
||||||
|
|
||||||
return []*cobra.Command{extensionCmd}
|
|
||||||
}
|
|
||||||
@@ -22,29 +22,29 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
"github.com/jedib0t/go-pretty/v6/text"
|
"github.com/jedib0t/go-pretty/v6/text"
|
||||||
"github.com/rsteube/carapace"
|
"github.com/rsteube/carapace"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
|
"github.com/bishopfox/sliver/client/command/settings"
|
||||||
|
"github.com/bishopfox/sliver/client/console"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtensionsCmd - List information about installed extensions.
|
// ExtensionsCmd - List information about installed extensions
|
||||||
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverClient) {
|
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
if len(GetAllExtensionManifests()) > 0 {
|
if 0 < len(getInstalledManifests()) {
|
||||||
PrintExtensions(con)
|
PrintExtensions(con)
|
||||||
} else {
|
} else {
|
||||||
con.PrintInfof("No extensions installed, use the 'armory' command to automatically install some\n")
|
con.PrintInfof("No extensions installed, use the 'armory' command to automatically install some\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintExtensions - Print a list of loaded extensions.
|
// PrintExtensions - Print a list of loaded extensions
|
||||||
func PrintExtensions(con *console.SliverClient) {
|
func PrintExtensions(con *console.SliverConsoleClient) {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
tw.SetStyle(settings.GetTableStyle(con))
|
||||||
tw.AppendHeader(table.Row{
|
tw.AppendHeader(table.Row{
|
||||||
@@ -68,12 +68,8 @@ func PrintExtensions(con *console.SliverClient) {
|
|||||||
for _, extension := range loadedExtensions {
|
for _, extension := range loadedExtensions {
|
||||||
//for _, extension := range extensionm.ExtCommand {
|
//for _, extension := range extensionm.ExtCommand {
|
||||||
installed := ""
|
installed := ""
|
||||||
//if _, ok := installedManifests[extension.Manifest.Name]; ok {
|
if _, ok := installedManifests[extension.Manifest.Name]; ok {
|
||||||
for _, installedManifest := range installedManifests {
|
installed = "✅"
|
||||||
if extension.Manifest.RootPath == installedManifest.RootPath {
|
|
||||||
installed = "✅"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tw.AppendRow(table.Row{
|
tw.AppendRow(table.Row{
|
||||||
extension.Manifest.Name,
|
extension.Manifest.Name,
|
||||||
@@ -102,9 +98,6 @@ func extensionPlatforms(extension *ExtCommand) []string {
|
|||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInstalledManifests - Returns a mapping of extension names to their parsed manifest objects.
|
|
||||||
// Reads all installed extension manifests from disk, ignoring any that cannot be read or parsed.
|
|
||||||
// The returned manifests have their RootPath set to the directory containing their manifest file.
|
|
||||||
func getInstalledManifests() map[string]*ExtensionManifest {
|
func getInstalledManifests() map[string]*ExtensionManifest {
|
||||||
manifestPaths := assets.GetInstalledExtensionManifests()
|
manifestPaths := assets.GetInstalledExtensionManifests()
|
||||||
installedManifests := map[string]*ExtensionManifest{}
|
installedManifests := map[string]*ExtensionManifest{}
|
||||||
@@ -118,50 +111,13 @@ func getInstalledManifests() map[string]*ExtensionManifest {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
manifest.RootPath = filepath.Dir(manifestPath)
|
|
||||||
installedManifests[manifest.Name] = manifest
|
installedManifests[manifest.Name] = manifest
|
||||||
}
|
}
|
||||||
return installedManifests
|
return installedManifests
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTemporarilyLoadedManifests returns a map of extension manifests that are currently
|
// ExtensionsCommandNameCompleter - Completer for installed extensions command names
|
||||||
// loaded into memory but not permanently installed. The map is keyed by the manifest's
|
func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||||
// Name field.
|
|
||||||
func getTemporarilyLoadedManifests() map[string]*ExtensionManifest {
|
|
||||||
tempManifests := map[string]*ExtensionManifest{}
|
|
||||||
for name, manifest := range loadedManifests {
|
|
||||||
tempManifests[name] = manifest
|
|
||||||
}
|
|
||||||
return tempManifests
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllExtensionManifests returns a combined list of manifest file paths from
|
|
||||||
// both installed and temporarily loaded extensions
|
|
||||||
func GetAllExtensionManifests() []string {
|
|
||||||
manifestPaths := make(map[string]struct{}) // use map for deduplication
|
|
||||||
|
|
||||||
// Add installed manifests
|
|
||||||
for _, manifest := range getInstalledManifests() {
|
|
||||||
manifestPath := filepath.Join(manifest.RootPath, ManifestFileName)
|
|
||||||
manifestPaths[manifestPath] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add temporarily loaded manifests
|
|
||||||
for _, manifest := range getTemporarilyLoadedManifests() {
|
|
||||||
manifestPath := filepath.Join(manifest.RootPath, ManifestFileName)
|
|
||||||
manifestPaths[manifestPath] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to slice
|
|
||||||
paths := make([]string, 0, len(manifestPaths))
|
|
||||||
for path := range manifestPaths {
|
|
||||||
paths = append(paths, path)
|
|
||||||
}
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtensionsCommandNameCompleter - Completer for installed extensions command names.
|
|
||||||
func ExtensionsCommandNameCompleter(con *console.SliverClient) carapace.Action {
|
|
||||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||||
//installedManifests := getInstalledManifests()
|
//installedManifests := getInstalledManifests()
|
||||||
results := []string{}
|
results := []string{}
|
||||||
|
|||||||
@@ -25,14 +25,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/util"
|
"github.com/bishopfox/sliver/util"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtensionsInstallCmd - Install an extension.
|
// ExtensionsInstallCmd - Install an extension
|
||||||
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
extLocalPath := args[0]
|
extLocalPath := args[0]
|
||||||
|
|
||||||
_, err := os.Stat(extLocalPath)
|
_, err := os.Stat(extLocalPath)
|
||||||
@@ -40,26 +41,11 @@ func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []
|
|||||||
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
|
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
InstallFromDir(extLocalPath, true, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
|
InstallFromDir(extLocalPath, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstallFromDir installs a Sliver extension from either a local directory or gzipped archive.
|
// Install an extension from a directory
|
||||||
// It reads the extension manifest, validates it, and copies all required files to the extensions
|
func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz bool) {
|
||||||
// directory. If an extension with the same name already exists, it can optionally prompt for
|
|
||||||
// overwrite confirmation.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - extLocalPath: Path to the source directory or gzipped archive containing the extension
|
|
||||||
// - promptToOverwrite: If true, prompts for confirmation before overwriting existing extension
|
|
||||||
// - con: Sliver console client for displaying status and error messages
|
|
||||||
// - isGz: Whether the source is a gzipped archive (true) or directory (false)
|
|
||||||
//
|
|
||||||
// The function will return early with error messages printed to console if:
|
|
||||||
// - The manifest cannot be read or parsed
|
|
||||||
// - Required directories cannot be created
|
|
||||||
// - File copy operations fail
|
|
||||||
// - User declines overwrite when prompted
|
|
||||||
func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.SliverClient, isGz bool) {
|
|
||||||
var manifestData []byte
|
var manifestData []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -79,24 +65,15 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use package name if available, otherwise use extension name
|
|
||||||
// (Note, for v1 manifests this will actually be command_name)
|
|
||||||
packageID := manifestF.Name
|
|
||||||
if manifestF.PackageName != "" {
|
|
||||||
packageID = manifestF.PackageName
|
|
||||||
}
|
|
||||||
|
|
||||||
//create repo path
|
//create repo path
|
||||||
minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(packageID))
|
minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestF.Name))
|
||||||
if _, err := os.Stat(minstallPath); !os.IsNotExist(err) {
|
if _, err := os.Stat(minstallPath); !os.IsNotExist(err) {
|
||||||
if promptToOverwrite {
|
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
|
||||||
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
|
confirm := false
|
||||||
confirm := false
|
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
survey.AskOne(prompt, &confirm)
|
||||||
survey.AskOne(prompt, &confirm)
|
if !confirm {
|
||||||
if !confirm {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
forceRemoveAll(minstallPath)
|
forceRemoveAll(minstallPath)
|
||||||
}
|
}
|
||||||
@@ -118,7 +95,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
|
|||||||
for _, manifestFile := range manifest.Files {
|
for _, manifestFile := range manifest.Files {
|
||||||
if manifestFile.Path != "" {
|
if manifestFile.Path != "" {
|
||||||
if isGz {
|
if isGz {
|
||||||
err = installArtifact(extLocalPath, installPath, manifestFile.Path)
|
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
|
||||||
} else {
|
} else {
|
||||||
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
|
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
|
||||||
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
|
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
|
||||||
@@ -130,7 +107,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
|
|||||||
}
|
}
|
||||||
err = util.CopyFile(src, dst)
|
err = util.CopyFile(src, dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("error copying file '%s' -> '%s': %s", src, dst, err)
|
err = fmt.Errorf("error copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -143,7 +120,7 @@ func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.Sl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string) error {
|
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
|
||||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath))
|
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -20,113 +20,15 @@ package extensions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/settings"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtensionMatch holds the details of a matched extension
|
// ExtensionsListCmd - List all extension loaded on the active session/beacon
|
||||||
|
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
type ExtensionMatch struct {
|
|
||||||
CommandName string
|
|
||||||
Hash string
|
|
||||||
BinPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindExtensionMatches searches through loaded extensions for matching hashes
|
|
||||||
// Returns a map of hash to ExtensionMatch (match will be nil if hash wasn't found)
|
|
||||||
func FindExtensionMatches(targetHashes []string) map[string]*ExtensionMatch {
|
|
||||||
results := make(map[string]*ExtensionMatch)
|
|
||||||
pathCache := make(map[string]*ExtensionMatch)
|
|
||||||
|
|
||||||
// Initialize results map with all target hashes
|
|
||||||
for _, hash := range targetHashes {
|
|
||||||
results[hash] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for matches
|
|
||||||
for _, extCmd := range loadedExtensions {
|
|
||||||
if extCmd == nil || len(extCmd.Files) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range extCmd.Files {
|
|
||||||
fullPath := filepath.Join(extCmd.Manifest.RootPath, file.Path)
|
|
||||||
|
|
||||||
// Check cache first
|
|
||||||
var match *ExtensionMatch
|
|
||||||
if cached, exists := pathCache[fullPath]; exists {
|
|
||||||
match = cached
|
|
||||||
} else {
|
|
||||||
// Calculate hash if not cached
|
|
||||||
fileData, err := os.ReadFile(fullPath)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
hashBytes := sha256.Sum256(fileData)
|
|
||||||
fileHash := hex.EncodeToString(hashBytes[:])
|
|
||||||
|
|
||||||
match = &ExtensionMatch{
|
|
||||||
CommandName: extCmd.CommandName,
|
|
||||||
Hash: fileHash,
|
|
||||||
BinPath: fullPath,
|
|
||||||
}
|
|
||||||
pathCache[fullPath] = match
|
|
||||||
}
|
|
||||||
|
|
||||||
if slices.Contains(targetHashes, match.Hash) {
|
|
||||||
results[match.Hash] = match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintExtensionMatches prints the extension matches in a formatted table
|
|
||||||
func PrintExtensionMatches(matches map[string]*ExtensionMatch, con *console.SliverClient) {
|
|
||||||
tw := table.NewWriter()
|
|
||||||
tw.SetStyle(settings.GetTableStyle(con))
|
|
||||||
tw.AppendHeader(table.Row{
|
|
||||||
"Command Name",
|
|
||||||
"Sha256 Hash",
|
|
||||||
"Bin Path",
|
|
||||||
})
|
|
||||||
tw.SortBy([]table.SortBy{
|
|
||||||
{Name: "Command Name", Mode: table.Asc},
|
|
||||||
})
|
|
||||||
|
|
||||||
for hash, match := range matches {
|
|
||||||
if match != nil {
|
|
||||||
tw.AppendRow(table.Row{
|
|
||||||
match.CommandName,
|
|
||||||
hash,
|
|
||||||
match.BinPath,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
tw.AppendRow(table.Row{
|
|
||||||
"",
|
|
||||||
hash,
|
|
||||||
"",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
con.Println(tw.Render())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtensionsListCmd - List all extension loaded on the active session/beacon.
|
|
||||||
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|
||||||
session := con.ActiveTarget.GetSessionInteractive()
|
session := con.ActiveTarget.GetSessionInteractive()
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return
|
return
|
||||||
@@ -144,12 +46,10 @@ func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []str
|
|||||||
con.PrintErrorf("%s\n", extList.Response.Err)
|
con.PrintErrorf("%s\n", extList.Response.Err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(extList.Names) > 0 {
|
||||||
if len(extList.Names) == 0 {
|
con.PrintInfof("Loaded extensions:\n")
|
||||||
return
|
for _, ext := range extList.Names {
|
||||||
|
con.Printf("- %s\n", ext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
con.PrintInfof("Loaded extensions:\n\n")
|
|
||||||
matches := FindExtensionMatches(extList.Names)
|
|
||||||
PrintExtensionMatches(matches, con)
|
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,6 @@ package extensions
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -34,26 +32,26 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
|
||||||
"github.com/bishopfox/sliver/client/command/help"
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
|
||||||
consts "github.com/bishopfox/sliver/client/constants"
|
|
||||||
"github.com/bishopfox/sliver/client/core"
|
|
||||||
"github.com/bishopfox/sliver/client/packages"
|
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
|
||||||
"github.com/bishopfox/sliver/util"
|
|
||||||
appConsole "github.com/reeflective/console"
|
appConsole "github.com/reeflective/console"
|
||||||
"github.com/rsteube/carapace"
|
"github.com/rsteube/carapace"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
|
"github.com/bishopfox/sliver/client/command/help"
|
||||||
|
"github.com/bishopfox/sliver/client/console"
|
||||||
|
consts "github.com/bishopfox/sliver/client/constants"
|
||||||
|
"github.com/bishopfox/sliver/client/core"
|
||||||
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
|
"github.com/bishopfox/sliver/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultTimeout = 60
|
defaultTimeout = 60
|
||||||
|
|
||||||
// ManifestFileName - Extension manifest file name.
|
// ManifestFileName - Extension manifest file name
|
||||||
ManifestFileName = "extension.json"
|
ManifestFileName = "extension.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,7 +78,6 @@ type ExtensionManifest_ struct {
|
|||||||
|
|
||||||
type ExtensionManifest struct {
|
type ExtensionManifest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PackageName string `json:"package_name"`
|
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ExtensionAuthor string `json:"extension_author"`
|
ExtensionAuthor string `json:"extension_author"`
|
||||||
OriginalAuthor string `json:"original_author"`
|
OriginalAuthor string `json:"original_author"`
|
||||||
@@ -88,21 +85,17 @@ type ExtensionManifest struct {
|
|||||||
|
|
||||||
ExtCommand []*ExtCommand `json:"commands"`
|
ExtCommand []*ExtCommand `json:"commands"`
|
||||||
|
|
||||||
RootPath string `json:"-"`
|
RootPath string `json:"-"`
|
||||||
ArmoryName string `json:"-"`
|
|
||||||
ArmoryPK string `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtCommand struct {
|
type ExtCommand struct {
|
||||||
CommandName string `json:"command_name"`
|
CommandName string `json:"command_name"`
|
||||||
Help string `json:"help"`
|
Help string `json:"help"`
|
||||||
LongHelp string `json:"long_help"`
|
LongHelp string `json:"long_help"`
|
||||||
Files []*extensionFile `json:"files"`
|
Files []*extensionFile `json:"files"`
|
||||||
Arguments []*extensionArgument `json:"arguments"`
|
Arguments []*extensionArgument `json:"arguments"`
|
||||||
Entrypoint string `json:"entrypoint"`
|
Entrypoint string `json:"entrypoint"`
|
||||||
DependsOn string `json:"depends_on"`
|
DependsOn string `json:"depends_on"`
|
||||||
Init string `json:"init"`
|
|
||||||
Schema *packages.OutputSchema `json:"schema"`
|
|
||||||
|
|
||||||
Manifest *ExtensionManifest
|
Manifest *ExtensionManifest
|
||||||
}
|
}
|
||||||
@@ -116,24 +109,17 @@ type extensionFile struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type extensionArgument struct {
|
type extensionArgument struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Desc string `json:"desc"`
|
Desc string `json:"desc"`
|
||||||
Optional bool `json:"optional"`
|
Optional bool `json:"optional"`
|
||||||
Default interface{} `json:"default,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (string, error) {
|
func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (string, error) {
|
||||||
filePath := ""
|
filePath := ""
|
||||||
for _, extFile := range e.Files {
|
for _, extFile := range e.Files {
|
||||||
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
||||||
if e.Manifest.RootPath != "" {
|
filePath = path.Join(assets.GetExtensionsDir(), e.Manifest.Name, extFile.Path)
|
||||||
// Use RootPath for temporarily loaded extensions
|
|
||||||
filePath = path.Join(e.Manifest.RootPath, extFile.Path)
|
|
||||||
} else {
|
|
||||||
// Fall back to extensions dir for installed extensions
|
|
||||||
filePath = path.Join(assets.GetExtensionsDir(), e.Manifest.Name, extFile.Path)
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,26 +134,17 @@ func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (strin
|
|||||||
return filePath, nil
|
return filePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtensionLoadCmd - Temporarily installs an extension from a local directory into the client.
|
// ExtensionLoadCmd - Load extension command
|
||||||
// The extension must contain a valid manifest file. If commands from the extension
|
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
// already exist, the user will be prompted to overwrite them.
|
|
||||||
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|
||||||
dirPath := args[0]
|
dirPath := args[0]
|
||||||
|
// dirPath := ctx.Args.String("dir-path")
|
||||||
// Add directory check
|
manyfest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||||
fileInfo, err := os.Stat(dirPath)
|
|
||||||
if err != nil || !fileInfo.IsDir() {
|
|
||||||
con.PrintErrorf("Path is not a directory: %s\n", dirPath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// do not add if the command already exists
|
// do not add if the command already exists
|
||||||
sliverMenu := con.App.Menu("implant")
|
sliverMenu := con.App.Menu("implant")
|
||||||
for _, extCmd := range manifest.ExtCommand {
|
for _, extCmd := range manyfest.ExtCommand {
|
||||||
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
||||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||||
confirm := false
|
confirm := false
|
||||||
@@ -182,11 +159,7 @@ func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadExtensionManifest loads and parses an extension manifest file from the given path.
|
// LoadExtensionManifest - Parse extension files
|
||||||
// It registers each command defined in the manifest into the loadedExtensions map
|
|
||||||
// and registers the complete manifest into loadedManifests. A single manifest may
|
|
||||||
// contain multiple extension commands. The manifest's RootPath is set to its containing
|
|
||||||
// directory. Returns the parsed manifest and any errors encountered.
|
|
||||||
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||||
data, err := os.ReadFile(manifestPath)
|
data, err := os.ReadFile(manifestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -208,7 +181,7 @@ func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
|||||||
|
|
||||||
func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
|
func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
|
||||||
ret := &ExtensionManifest{
|
ret := &ExtensionManifest{
|
||||||
Name: old.CommandName, //treating old command name as the manifest name to avoid weird chars mostly
|
Name: old.CommandName, //treating old commandname as the manifest name to avoid weird chars mostly
|
||||||
Version: old.Version,
|
Version: old.Version,
|
||||||
ExtensionAuthor: old.ExtensionAuthor,
|
ExtensionAuthor: old.ExtensionAuthor,
|
||||||
OriginalAuthor: old.OriginalAuthor,
|
OriginalAuthor: old.OriginalAuthor,
|
||||||
@@ -224,7 +197,6 @@ func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
|
|||||||
Entrypoint: old.Entrypoint,
|
Entrypoint: old.Entrypoint,
|
||||||
Files: old.Files,
|
Files: old.Files,
|
||||||
Arguments: old.Arguments,
|
Arguments: old.Arguments,
|
||||||
Schema: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -240,9 +212,7 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
|||||||
err := json.Unmarshal(data, &extManifest)
|
err := json.Unmarshal(data, &extManifest)
|
||||||
if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible
|
if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible
|
||||||
//maybe it's an old manifest
|
//maybe it's an old manifest
|
||||||
if err != nil {
|
log.Println(err)
|
||||||
log.Printf("extension load error: %s", err)
|
|
||||||
}
|
|
||||||
oldmanifest := &ExtensionManifest_{}
|
oldmanifest := &ExtensionManifest_{}
|
||||||
err := json.Unmarshal(data, &oldmanifest)
|
err := json.Unmarshal(data, &oldmanifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -252,13 +222,9 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
|||||||
//yes, ok, lets jigger it to a new manifest
|
//yes, ok, lets jigger it to a new manifest
|
||||||
extManifest = convertOldManifest(oldmanifest)
|
extManifest = convertOldManifest(oldmanifest)
|
||||||
}
|
}
|
||||||
//pass ref to manifest to each command and initialize output schema if applicable
|
//pass ref to manifest to each command
|
||||||
for i := range extManifest.ExtCommand {
|
for i := range extManifest.ExtCommand {
|
||||||
command := extManifest.ExtCommand[i]
|
extManifest.ExtCommand[i].Manifest = extManifest
|
||||||
command.Manifest = extManifest
|
|
||||||
if command.Schema != nil {
|
|
||||||
command.Schema.IngestColumns()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return extManifest, validManifest(extManifest)
|
return extManifest, validManifest(extManifest)
|
||||||
}
|
}
|
||||||
@@ -291,29 +257,19 @@ func validManifest(manifest *ExtensionManifest) error {
|
|||||||
if extManifest.Help == "" {
|
if extManifest.Help == "" {
|
||||||
return errors.New("missing `help` field in extension manifest")
|
return errors.New("missing `help` field in extension manifest")
|
||||||
}
|
}
|
||||||
if extManifest.Schema != nil {
|
|
||||||
if !packages.IsValidSchemaType(extManifest.Schema.Name) {
|
|
||||||
return fmt.Errorf("%s is not a valid schema type", extManifest.Schema.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtensionRegisterCommand adds an extension command to the cobra command system.
|
// ExtensionRegisterCommand - Register a new extension command
|
||||||
// It validates the extension's arguments, updates the loadedExtensions map, and
|
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
// creates a cobra.Command with proper usage text, help documentation, and argument
|
|
||||||
// handling. The command is added as a subcommand to the provided parent cobra.Command.
|
|
||||||
// Arguments are displayed in the help text as uppercase, with optional args in square
|
|
||||||
// brackets. The help text includes sections for command usage, description, and detailed
|
|
||||||
// argument specifications.
|
|
||||||
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverClient) {
|
|
||||||
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
|
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
|
||||||
con.PrintErrorf(errInvalidArgs.Error())
|
con.PrintErrorf(errInvalidArgs.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loadedExtensions[extCmd.CommandName] = extCmd
|
loadedExtensions[extCmd.CommandName] = extCmd
|
||||||
|
//helpMsg := extCmd.Help
|
||||||
|
|
||||||
usage := strings.Builder{}
|
usage := strings.Builder{}
|
||||||
usage.WriteString(extCmd.CommandName)
|
usage.WriteString(extCmd.CommandName)
|
||||||
@@ -365,9 +321,9 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
|
|||||||
|
|
||||||
// Command
|
// Command
|
||||||
extensionCmd := &cobra.Command{
|
extensionCmd := &cobra.Command{
|
||||||
Use: usage.String(),
|
Use: usage.String(), //extCmd.CommandName,
|
||||||
Short: extCmd.Help,
|
//Short: helpMsg.String(), doesn't appear to be used?
|
||||||
Long: help.FormatHelpTmpl(longHelp.String()),
|
Long: help.FormatHelpTmpl(longHelp.String()),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
runExtensionCmd(cmd, con, args)
|
runExtensionCmd(cmd, con, args)
|
||||||
},
|
},
|
||||||
@@ -389,7 +345,7 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
|
|||||||
cmd.AddCommand(extensionCmd)
|
cmd.AddCommand(extensionCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand, cmd *cobra.Command, con *console.SliverClient) error {
|
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||||
var extensionList []string
|
var extensionList []string
|
||||||
binPath, err := ext.getFileForTarget(goos, goarch)
|
binPath, err := ext.getFileForTarget(goos, goarch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -410,50 +366,27 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
|
|||||||
}
|
}
|
||||||
extensionList = extList.Names
|
extensionList = extList.Names
|
||||||
}
|
}
|
||||||
//extensionList contains all *implant* loaded extensions. Is a sha256 sum of the relevant file.
|
depLoaded := false
|
||||||
|
|
||||||
//get the file hash to compare against implant extensions later
|
|
||||||
toberunfilepath, err := ext.getFileForTarget(goos, goarch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
//we need to check the dependent if it's a bof
|
|
||||||
if ext.DependsOn != "" {
|
|
||||||
//verify extension is in loaded list
|
|
||||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
|
||||||
toberunfilepath, err = extension.getFileForTarget(goos, goarch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// handle error
|
|
||||||
return fmt.Errorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo, maybe cache these values somewhere
|
|
||||||
toberunfiledata, err := os.ReadFile(toberunfilepath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(toberunfiledata) == 0 {
|
|
||||||
//read an empty file, bail out
|
|
||||||
return errors.New("read empty extension file content")
|
|
||||||
}
|
|
||||||
bd := sha256.Sum256(toberunfiledata)
|
|
||||||
toberunhash := hex.EncodeToString(bd[:])
|
|
||||||
|
|
||||||
for _, extName := range extensionList {
|
for _, extName := range extensionList {
|
||||||
//check if extension we are trying to run (ext.CommandName or ext.DependsOn, for bofs) is already loaded
|
if !depLoaded {
|
||||||
if extName == toberunhash {
|
if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn {
|
||||||
//exists on the other side, exit early
|
depLoaded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Extension not found, let's load it
|
// Extension not found, let's load it
|
||||||
// BOFs are not loaded by the DLL loader, but we need to load the loader (more load more good)
|
if filepath.Ext(binPath) == ".o" {
|
||||||
if ext.DependsOn != "" {
|
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
|
||||||
return loadDep(goos, goarch, ext.DependsOn, cmd, con)
|
// Auto load the coff loader if we have it
|
||||||
|
if !depLoaded {
|
||||||
|
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
|
||||||
|
return errLoad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
binData, err := os.ReadFile(binPath)
|
binData, err := os.ReadFile(binPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -465,45 +398,14 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) error {
|
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||||
//set extension name to a hash of the data to avoid loading more than one instance
|
|
||||||
bd := sha256.Sum256(binData)
|
|
||||||
name := hex.EncodeToString(bd[:])
|
|
||||||
sess, beac := con.ActiveTarget.GetInteractive()
|
|
||||||
ctrl := make(chan bool)
|
|
||||||
//first time run of an extension will require some waiting depending on the size
|
|
||||||
if sess != nil {
|
|
||||||
msg := fmt.Sprintf("Sending %s to implant ...", ext.CommandName)
|
|
||||||
con.SpinUntil(msg, ctrl)
|
|
||||||
}
|
|
||||||
//don't block if we are in beacon mode
|
|
||||||
if beac != nil && sess == nil {
|
|
||||||
go func() {
|
|
||||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
|
||||||
Name: name,
|
|
||||||
Data: binData,
|
|
||||||
OS: goos,
|
|
||||||
Init: ext.Init,
|
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("Error registering extension: %s\n", err)
|
|
||||||
}
|
|
||||||
if registerResp.Response != nil && registerResp.Response.Err != "" {
|
|
||||||
con.PrintErrorf("Error registering extension: %s\n", errors.New(registerResp.Response.Err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
//session mode (hopefully)
|
|
||||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||||
Name: name,
|
Name: ext.CommandName,
|
||||||
Data: binData,
|
Data: binData,
|
||||||
OS: goos,
|
OS: goos,
|
||||||
|
//Init: ext.Init, ?
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
})
|
})
|
||||||
ctrl <- true
|
|
||||||
<-ctrl
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -513,7 +415,7 @@ func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverClient) error {
|
func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||||
depExt, ok := loadedExtensions[depName]
|
depExt, ok := loadedExtensions[depName]
|
||||||
if ok {
|
if ok {
|
||||||
depBinPath, err := depExt.getFileForTarget(goos, goarch)
|
depBinPath, err := depExt.getFileForTarget(goos, goarch)
|
||||||
@@ -529,7 +431,7 @@ func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con
|
|||||||
return fmt.Errorf("missing dependency %s", depName)
|
return fmt.Errorf("missing dependency %s", depName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
extensionArgs []byte
|
extensionArgs []byte
|
||||||
@@ -585,11 +487,17 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
extName = ext.DependsOn
|
extName = ext.DependsOn
|
||||||
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
|
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
|
||||||
} else {
|
} else {
|
||||||
// Regular DLL - Just join the arguments with spaces
|
// Regular DLL
|
||||||
if len(args) > 0 {
|
// extArgs := strings.Join(args, " ")
|
||||||
|
//legacy case - single string arg
|
||||||
|
if len(ext.Arguments) == 1 && ext.Arguments[0].Type == "string" {
|
||||||
extensionArgs = []byte(strings.Join(args, " "))
|
extensionArgs = []byte(strings.Join(args, " "))
|
||||||
} else {
|
} else {
|
||||||
extensionArgs = []byte{}
|
extensionArgs, err = getExtArgs(cmd, args, binPath, ext)
|
||||||
|
if err != nil {
|
||||||
|
con.PrintErrorf("ext args error: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
extName = ext.CommandName
|
extName = ext.CommandName
|
||||||
entryPoint = ext.Entrypoint
|
entryPoint = ext.Entrypoint
|
||||||
@@ -598,41 +506,8 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
msg := fmt.Sprintf("Executing %s ...", cmd.Name())
|
msg := fmt.Sprintf("Executing %s ...", cmd.Name())
|
||||||
con.SpinUntil(msg, ctrl)
|
con.SpinUntil(msg, ctrl)
|
||||||
extdata, err := os.ReadFile(binPath)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("ext read file error: %s\n", err)
|
|
||||||
}
|
|
||||||
bd := sha256.Sum256(extdata)
|
|
||||||
name := hex.EncodeToString(bd[:])
|
|
||||||
if isBOF {
|
|
||||||
//if we are using a bof, we are actually calling the coffloader extension - so get the file from dep ref and use that shasum
|
|
||||||
|
|
||||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
|
||||||
dep, err := extension.getFileForTarget(goos, goarch)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("could not get file for extension %s", ext.DependsOn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
depdata, err := os.ReadFile(dep)
|
|
||||||
if err != nil {
|
|
||||||
con.PrintErrorf("dep read file error: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(depdata) == 0 {
|
|
||||||
con.PrintErrorf("read empty file: %s\n", dep)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bd = sha256.Sum256(depdata)
|
|
||||||
name = hex.EncodeToString(bd[:])
|
|
||||||
} else {
|
|
||||||
// handle error
|
|
||||||
con.PrintErrorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||||
Name: name,
|
Name: extName,
|
||||||
Export: entryPoint,
|
Export: entryPoint,
|
||||||
Args: extensionArgs,
|
Args: extensionArgs,
|
||||||
Request: con.ActiveTarget.Request(cmd),
|
Request: con.ActiveTarget.Request(cmd),
|
||||||
@@ -651,38 +526,23 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con)
|
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
|
||||||
})
|
})
|
||||||
con.PrintAsyncResponse(callExtResp.Response)
|
con.PrintAsyncResponse(callExtResp.Response)
|
||||||
} else {
|
} else {
|
||||||
PrintExtOutput(extName, ext.CommandName, ext.Schema, callExtResp, con)
|
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintExtOutput - Print the ext execution output.
|
// PrintExtOutput - Print the ext execution output
|
||||||
func PrintExtOutput(extName string, commandName string, outputSchema *packages.OutputSchema, callExtension *sliverpb.CallExtension, con *console.SliverClient) {
|
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
|
||||||
if extName == commandName {
|
if extName == commandName {
|
||||||
con.PrintInfof("Successfully executed %s\n", extName)
|
con.PrintInfof("Successfully executed %s", extName)
|
||||||
} else {
|
} else {
|
||||||
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName)
|
con.PrintInfof("Successfully executed %s (%s)", commandName, extName)
|
||||||
}
|
}
|
||||||
if 0 < len(string(callExtension.Output)) {
|
if 0 < len(string(callExtension.Output)) {
|
||||||
if outputSchema == nil {
|
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
||||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
|
||||||
} else {
|
|
||||||
// Get output schema
|
|
||||||
schema := packages.GetNewPackageOutput(outputSchema.Name)
|
|
||||||
if schema != nil {
|
|
||||||
ingestErr := schema.IngestData(callExtension.Output, outputSchema.Columns(), outputSchema.GroupBy)
|
|
||||||
if ingestErr != nil {
|
|
||||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
|
||||||
} else {
|
|
||||||
con.Printf("%s\n", schema.CreateTable())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if callExtension.Response != nil && callExtension.Response.Err != "" {
|
if callExtension.Response != nil && callExtension.Response.Err != "" {
|
||||||
con.PrintErrorf(callExtension.Response.Err)
|
con.PrintErrorf(callExtension.Response.Err)
|
||||||
@@ -690,7 +550,7 @@ func PrintExtOutput(extName string, commandName string, outputSchema *packages.O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) {
|
func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtCommand) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
argsBuffer := core.BOFArgsBuffer{
|
argsBuffer := core.BOFArgsBuffer{
|
||||||
Buffer: new(bytes.Buffer),
|
Buffer: new(bytes.Buffer),
|
||||||
@@ -699,12 +559,6 @@ func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]b
|
|||||||
// Parse BOF arguments from grumble
|
// Parse BOF arguments from grumble
|
||||||
missingRequiredArgs := make([]string, 0)
|
missingRequiredArgs := make([]string, 0)
|
||||||
|
|
||||||
// If we have an extension that expects a single string, but more than one has been parsed, combine them
|
|
||||||
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") && len(args) > 0 {
|
|
||||||
// The loop below will only read the first element of args because ext.Arguments is 1
|
|
||||||
args[0] = strings.Join(args, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arg := range ext.Arguments {
|
for _, arg := range ext.Arguments {
|
||||||
// If we don't have any positional words left to consume,
|
// If we don't have any positional words left to consume,
|
||||||
// add the remaining required extension arguments in the
|
// add the remaining required extension arguments in the
|
||||||
@@ -797,7 +651,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
parsedArgs, err := ParseFlagArgumentsToBuffer(cmd, args, binPath, ext)
|
parsedArgs, err := getExtArgs(cmd, args, binPath, ext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -812,7 +666,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
|
|||||||
return extensionArgs, nil
|
return extensionArgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdExists - checks if a command exists.
|
// CmdExists - checks if a command exists
|
||||||
func CmdExists(name string, cmd *cobra.Command) bool {
|
func CmdExists(name string, cmd *cobra.Command) bool {
|
||||||
for _, c := range cmd.Commands() {
|
for _, c := range cmd.Commands() {
|
||||||
if name == c.Name() {
|
if name == c.Name() {
|
||||||
|
|||||||
@@ -23,17 +23,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/assets"
|
"github.com/bishopfox/sliver/client/assets"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/util"
|
"github.com/bishopfox/sliver/util"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtensionsRemoveCmd - Remove an extension.
|
// ExtensionsRemoveCmd - Remove an extension
|
||||||
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
if name == "" {
|
if name == "" {
|
||||||
con.PrintErrorf("Extension name is required\n")
|
con.PrintErrorf("Extension name is required\n")
|
||||||
@@ -64,8 +64,8 @@ func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveExtensionByCommandName - Remove an extension by command name.
|
// RemoveExtensionByCommandName - Remove an extension by command name
|
||||||
func RemoveExtensionByCommandName(commandName string, con *console.SliverClient) error {
|
func RemoveExtensionByCommandName(commandName string, con *console.SliverConsoleClient) error {
|
||||||
if commandName == "" {
|
if commandName == "" {
|
||||||
return errors.New("command name is required")
|
return errors.New("command name is required")
|
||||||
}
|
}
|
||||||
@@ -82,40 +82,17 @@ func RemoveExtensionByCommandName(commandName string, con *console.SliverClient)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveExtensionByManifestName - remove by the named manifest, returns true if manifest was removed, false if no manifest with that name was found
|
// RemoveExtensionByManifestName - remove by the named manifest, returns true if manifest was removed, false if no manifest with that name was found
|
||||||
func RemoveExtensionByManifestName(manifestName string, con *console.SliverClient) (bool, error) {
|
func RemoveExtensionByManifestName(manifestName string, con *console.SliverConsoleClient) (bool, error) {
|
||||||
if manifestName == "" {
|
if manifestName == "" {
|
||||||
return false, errors.New("command name is required")
|
return false, errors.New("command name is required")
|
||||||
}
|
}
|
||||||
if man, ok := loadedManifests[manifestName]; ok {
|
if man, ok := loadedManifests[manifestName]; ok {
|
||||||
// Found the manifest
|
//foudn the manifest
|
||||||
var extPath string
|
//delet it
|
||||||
if man.RootPath != "" {
|
extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestName))
|
||||||
// Use RootPath for temporarily loaded extensions
|
|
||||||
extPath = man.RootPath
|
|
||||||
} else {
|
|
||||||
// Fall back to extensions dir for installed extensions
|
|
||||||
extPath = filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestName))
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(extPath); os.IsNotExist(err) {
|
if _, err := os.Stat(extPath); os.IsNotExist(err) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If path is outside extensions directory, prompt for confirmation
|
|
||||||
if !strings.HasPrefix(extPath, assets.GetExtensionsDir()) {
|
|
||||||
confirm := false
|
|
||||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' extension directory from filesystem?", manifestName)}
|
|
||||||
survey.AskOne(prompt, &confirm)
|
|
||||||
if !confirm {
|
|
||||||
// Skip the forceRemoveAll but continue with the rest
|
|
||||||
delete(loadedManifests, manifestName)
|
|
||||||
for _, cmd := range man.ExtCommand {
|
|
||||||
delete(loadedExtensions, cmd.CommandName)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
forceRemoveAll(extPath)
|
forceRemoveAll(extPath)
|
||||||
delete(loadedManifests, manifestName)
|
delete(loadedManifests, manifestName)
|
||||||
for _, cmd := range man.ExtCommand {
|
for _, cmd := range man.ExtCommand {
|
||||||
|
|||||||
@@ -23,22 +23,22 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma/formatters"
|
"github.com/alecthomas/chroma/formatters"
|
||||||
"github.com/alecthomas/chroma/lexers"
|
"github.com/alecthomas/chroma/lexers"
|
||||||
"github.com/alecthomas/chroma/styles"
|
"github.com/alecthomas/chroma/styles"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/loot"
|
"github.com/bishopfox/sliver/client/command/loot"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/bishopfox/sliver/util/encoders"
|
"github.com/bishopfox/sliver/util/encoders"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CatCmd - Display the contents of a remote file.
|
// CatCmd - Display the contents of a remote file
|
||||||
func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -73,16 +73,16 @@ func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PrintCat(filePath, download, cmd, con)
|
PrintCat(download, cmd, con)
|
||||||
})
|
})
|
||||||
con.PrintAsyncResponse(download.Response)
|
con.PrintAsyncResponse(download.Response)
|
||||||
} else {
|
} else {
|
||||||
PrintCat(filePath, download, cmd, con)
|
PrintCat(download, cmd, con)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintCat - Print the download to stdout.
|
// PrintCat - Print the download to stdout
|
||||||
func PrintCat(originalFileName string, download *sliverpb.Download, cmd *cobra.Command, con *console.SliverClient) {
|
func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
var (
|
var (
|
||||||
lootDownload bool = true
|
lootDownload bool = true
|
||||||
err error
|
err error
|
||||||
@@ -109,9 +109,6 @@ func PrintCat(originalFileName string, download *sliverpb.Download, cmd *cobra.C
|
|||||||
con.Printf("\n")
|
con.Printf("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !strings.Contains(download.Path, originalFileName) {
|
|
||||||
con.PrintInfof("Supplied pattern %s matched file %s\n\n", originalFileName, download.Path)
|
|
||||||
}
|
|
||||||
if color, _ := cmd.Flags().GetBool("colorize-output"); color {
|
if color, _ := cmd.Flags().GetBool("colorize-output"); color {
|
||||||
if err = colorize(download); err != nil {
|
if err = colorize(download); err != nil {
|
||||||
con.Println(string(download.Data))
|
con.Println(string(download.Data))
|
||||||
|
|||||||
@@ -21,15 +21,17 @@ package filesystem
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CdCmd - Change directory on the remote system.
|
// CdCmd - Change directory on the remote system
|
||||||
func CdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -20,15 +20,17 @@ package filesystem
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChmodCmd - Change the permissions of a file on the remote file system.
|
// ChmodCmd - Change the permissions of a file on the remote file system
|
||||||
func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -75,8 +77,8 @@ func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintChmod - Print the chmod response.
|
// PrintChmod - Print the chmod response
|
||||||
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverClient) {
|
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverConsoleClient) {
|
||||||
if chmod.Response != nil && chmod.Response.Err != "" {
|
if chmod.Response != nil && chmod.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", chmod.Response.Err)
|
con.PrintErrorf("%s\n", chmod.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -20,15 +20,17 @@ package filesystem
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChownCmd - Change the owner of a file on the remote file system.
|
// ChownCmd - Change the owner of a file on the remote file system
|
||||||
func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -83,8 +85,8 @@ func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintChown - Print the chown response.
|
// PrintChown - Print the chown response
|
||||||
func PrintChown(chown *sliverpb.Chown, con *console.SliverClient) {
|
func PrintChown(chown *sliverpb.Chown, con *console.SliverConsoleClient) {
|
||||||
if chown.Response != nil && chown.Response.Err != "" {
|
if chown.Response != nil && chown.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", chown.Response.Err)
|
con.PrintErrorf("%s\n", chown.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -21,15 +21,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChtimesCmd - Change the access and modified time of a file on the remote file system.
|
// ChtimesCmd - Change the access and modified time of a file on the remote file system
|
||||||
func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -96,8 +98,8 @@ func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintChtimes - Print the Chtimes response.
|
// PrintChtimes - Print the Chtimes response
|
||||||
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverClient) {
|
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverConsoleClient) {
|
||||||
if chtimes.Response != nil && chtimes.Response.Err != "" {
|
if chtimes.Response != nil && chtimes.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", chtimes.Response.Err)
|
con.PrintErrorf("%s\n", chtimes.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,14 +21,16 @@ package filesystem
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err error) {
|
func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -70,7 +72,7 @@ func CpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintCp(cp *sliverpb.Cp, con *console.SliverClient) {
|
func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) {
|
||||||
if cp.Response != nil && cp.Response.Err != "" {
|
if cp.Response != nil && cp.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", cp.Response.Err)
|
con.PrintErrorf("%s\n", cp.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -28,17 +28,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"gopkg.in/AlecAivazis/survey.v1"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/command/loot"
|
"github.com/bishopfox/sliver/client/command/loot"
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/bishopfox/sliver/util/encoders"
|
"github.com/bishopfox/sliver/util/encoders"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
"gopkg.in/AlecAivazis/survey.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func DownloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -97,12 +98,14 @@ func prettifyDownloadName(path string) string {
|
|||||||
filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_")
|
filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_")
|
||||||
|
|
||||||
// If there is an underscore at the front of the filename, strip that off
|
// If there is an underscore at the front of the filename, strip that off
|
||||||
filteredString, _ = strings.CutPrefix(filteredString, "_")
|
if strings.HasPrefix(filteredString, "_") {
|
||||||
|
filteredString = filteredString[1:]
|
||||||
|
}
|
||||||
|
|
||||||
return filteredString
|
return filteredString
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverClient) {
|
func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverConsoleClient) {
|
||||||
var err error
|
var err error
|
||||||
if download.Response != nil && download.Response.Err != "" {
|
if download.Response != nil && download.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", download.Response.Err)
|
con.PrintErrorf("%s\n", download.Response.Err)
|
||||||
@@ -116,15 +119,7 @@ func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use download.Path because a glob matching a single file on the remote will not have the
|
remotePath := args[0]
|
||||||
// correct file name - the filename will contain the globs if we use the path from the user
|
|
||||||
// On non-Windows systems, filepath.Base will not see backslashes, so we will replace them
|
|
||||||
// on systems that do not use backslashes as path separators
|
|
||||||
remotePath := download.Path
|
|
||||||
if strings.Contains(download.Path, "\\") && string(os.PathSeparator) != "\\" {
|
|
||||||
remotePath = strings.ReplaceAll(download.Path, "\\", "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
var localPath string
|
var localPath string
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
localPath = "."
|
localPath = "."
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ func processFlags(searchPattern string, insensitive bool, exact bool) string {
|
|||||||
return processedSearchPattern
|
return processedSearchPattern
|
||||||
}
|
}
|
||||||
|
|
||||||
func GrepCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -152,7 +152,7 @@ func GrepCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// printGrep - Print the results from the grep operation to stdout
|
// printGrep - Print the results from the grep operation to stdout
|
||||||
func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverClient) {
|
func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||||
lootName, _ := cmd.Flags().GetString("name")
|
lootName, _ := cmd.Flags().GetString("name")
|
||||||
colorize, _ := cmd.Flags().GetBool("colorize-output")
|
colorize, _ := cmd.Flags().GetBool("colorize-output")
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import (
|
|||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head bool) {
|
func HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string, head bool) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -63,7 +63,7 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
|
|||||||
} else {
|
} else {
|
||||||
operationName = "bytes"
|
operationName = "bytes"
|
||||||
}
|
}
|
||||||
} else {
|
} else if cmd.Flags().Changed("lines") {
|
||||||
fetchBytes = false
|
fetchBytes = false
|
||||||
fetchSize, _ = cmd.Flags().GetInt64("lines")
|
fetchSize, _ = cmd.Flags().GetInt64("lines")
|
||||||
if fetchSize < 0 {
|
if fetchSize < 0 {
|
||||||
@@ -76,6 +76,9 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
|
|||||||
} else {
|
} else {
|
||||||
operationName = "lines"
|
operationName = "lines"
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
con.PrintErrorf("A number of bytes or a number of lines must be specified.")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl := make(chan bool)
|
ctrl := make(chan bool)
|
||||||
@@ -115,10 +118,10 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head
|
|||||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PrintCat(filePath, download, cmd, con)
|
PrintCat(download, cmd, con)
|
||||||
})
|
})
|
||||||
con.PrintAsyncResponse(download.Response)
|
con.PrintAsyncResponse(download.Response)
|
||||||
} else {
|
} else {
|
||||||
PrintCat(filePath, download, cmd, con)
|
PrintCat(download, cmd, con)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,17 +27,19 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/bishopfox/sliver/util"
|
"github.com/bishopfox/sliver/util"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LsCmd - List the contents of a remote directory.
|
// LsCmd - List the contents of a remote directory
|
||||||
func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -73,8 +75,8 @@ func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintLs - Display an sliverpb.Ls object.
|
// PrintLs - Display an sliverpb.Ls object
|
||||||
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverClient) {
|
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverConsoleClient) {
|
||||||
if ls.Response != nil && ls.Response.Err != "" {
|
if ls.Response != nil && ls.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -20,15 +20,16 @@ package filesystem
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MemfilesAddCmd - Add memfile.
|
// MemfilesAddCmd - Add memfile
|
||||||
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -56,8 +57,8 @@ func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintAddMemfile - Print the memfiles response.
|
// PrintAddMemfile - Print the memfiles response
|
||||||
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverClient) {
|
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverConsoleClient) {
|
||||||
if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" {
|
if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", memfilesAdd.Response.Err)
|
con.PrintErrorf("%s\n", memfilesAdd.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -25,16 +25,17 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/bishopfox/sliver/util"
|
"github.com/bishopfox/sliver/util"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MemfilesListCmd - List memfiles.
|
// MemfilesListCmd - List memfiles
|
||||||
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -62,8 +63,8 @@ func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintMemfiles - Display an sliverpb.Ls object.
|
// PrintMemfiles - Display an sliverpb.Ls object
|
||||||
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverClient) {
|
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
|
||||||
if ls.Response != nil && ls.Response.Err != "" {
|
if ls.Response != nil && ls.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -21,15 +21,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/bishopfox/sliver/client/console"
|
"github.com/bishopfox/sliver/client/console"
|
||||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MemfilesRmCmd - Remove a memfile.
|
// MemfilesRmCmd - Remove a memfile
|
||||||
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||||
session, beacon := con.ActiveTarget.GetInteractive()
|
session, beacon := con.ActiveTarget.GetInteractive()
|
||||||
if session == nil && beacon == nil {
|
if session == nil && beacon == nil {
|
||||||
return
|
return
|
||||||
@@ -69,8 +70,8 @@ func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintRmMemfile - Remove a memfile.
|
// PrintRmMemfile - Remove a memfile
|
||||||
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverClient) {
|
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverConsoleClient) {
|
||||||
if memfilesList.Response != nil && memfilesList.Response.Err != "" {
|
if memfilesList.Response != nil && memfilesList.Response.Err != "" {
|
||||||
con.PrintErrorf("%s\n", memfilesList.Response.Err)
|
con.PrintErrorf("%s\n", memfilesList.Response.Err)
|
||||||
return
|
return
|
||||||
|
|||||||
Loaded 100 of 3804 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user