mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 01:38:17 -10:00
Compare commits
304
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a5970c3da | ||
|
|
2eb2ac8121 | ||
|
|
daf7d404b1 | ||
|
|
c73b183f74 | ||
|
|
7564312f5e | ||
|
|
6134b78afb | ||
|
|
3db9946608 | ||
|
|
ef09058498 | ||
|
|
331faeb8ef | ||
|
|
4ae195e3eb | ||
|
|
f8f8ba5864 | ||
|
|
216e3b9759 | ||
|
|
f1bc5074f9 | ||
|
|
5083faa1e1 | ||
|
|
1b5d3436eb | ||
|
|
56303eb89a | ||
|
|
9397fcb4ae | ||
|
|
04c6c3f800 | ||
|
|
7842a6c4c6 | ||
|
|
6c815a4432 | ||
|
|
81358e31bb | ||
|
|
7bea772dbd | ||
|
|
0b235b4d42 | ||
|
|
1402a75b18 | ||
|
|
ff5e4a4a24 | ||
|
|
e59c90c89a | ||
|
|
49771f0159 | ||
|
|
a2808b19f6 | ||
|
|
eddfb7073c | ||
|
|
930799495c | ||
|
|
5016fb8d7c | ||
|
|
65382f7b77 | ||
|
|
c1d00f0e48 | ||
|
|
e89280d64a | ||
|
|
aa4c9d7b8a | ||
|
|
2be820daf1 | ||
|
|
0a44fde9fa | ||
|
|
151dfe2643 | ||
|
|
fa7033f0c4 | ||
|
|
4eab1b582a | ||
|
|
f78db1a825 | ||
|
|
eb61740f5d | ||
|
|
39d0f3b838 | ||
|
|
8f084c99a4 | ||
|
|
87720d8a41 | ||
|
|
f8dd5230c1 | ||
|
|
9296a8abce | ||
|
|
abc8747a51 | ||
|
|
dcbfe10bc4 | ||
|
|
4336f933e0 | ||
|
|
6a4521a4b0 | ||
|
|
bf80f8f7e6 | ||
|
|
174455cbcc | ||
|
|
09564cf241 | ||
|
|
03fa4029d3 | ||
|
|
b33e012f2a | ||
|
|
98f13030f9 | ||
|
|
040863b757 | ||
|
|
1624c84eb6 | ||
|
|
44368b26ad | ||
|
|
0eb93d51c0 | ||
|
|
c7cd14eae2 | ||
|
|
9c9caba8d8 | ||
|
|
4b6219d860 | ||
|
|
a47aeb7e73 | ||
|
|
d3255ec7bb | ||
|
|
4c0e6cb585 | ||
|
|
88e35469b8 | ||
|
|
cd711c1e2f | ||
|
|
723eca2ddd | ||
|
|
0b2e0fa9bc | ||
|
|
9135afd201 | ||
|
|
b3a9ba86e1 | ||
|
|
2211b13c31 | ||
|
|
187394439e | ||
|
|
3b767baca3 | ||
|
|
c1070f917f | ||
|
|
5487df2000 | ||
|
|
97497c9f78 | ||
|
|
ee36676ba2 | ||
|
|
6809d81b66 | ||
|
|
6b876b0a0e | ||
|
|
33ab6096f1 | ||
|
|
0a6636fb71 | ||
|
|
6a30b8a99a | ||
|
|
a3c353baf7 | ||
|
|
5e992e073e | ||
|
|
12d2569c2f | ||
|
|
1abda712c4 | ||
|
|
6dc49f5074 | ||
|
|
2eff697a50 | ||
|
|
143246a9fc | ||
|
|
ab80379e5e | ||
|
|
d8e400f005 | ||
|
|
a4cc570845 | ||
|
|
0072335cc0 | ||
|
|
6bccd35318 | ||
|
|
f37bed31c0 | ||
|
|
dac592d231 | ||
|
|
f98ae8e7fe | ||
|
|
026c1d6ef6 | ||
|
|
4b982f422f | ||
|
|
c2f5460c63 | ||
|
|
214143e9d9 | ||
|
|
2607a021d8 | ||
|
|
e474dc452f | ||
|
|
c28e8e1e0f | ||
|
|
7b2822795c | ||
|
|
cc5f162848 | ||
|
|
83b6bf9147 | ||
|
|
79202297df | ||
|
|
cd4e967cd9 | ||
|
|
ea901f5442 | ||
|
|
a5cd0d0cae | ||
|
|
e9930d01fd | ||
|
|
f83131ef43 | ||
|
|
73d5c3192d | ||
|
|
076db57bba | ||
|
|
db85a76c85 | ||
|
|
662c1c1e18 | ||
|
|
5634b09921 | ||
|
|
f8c8cdfe31 | ||
|
|
cabe48e997 | ||
|
|
cabb1b09cc | ||
|
|
3bb667f814 | ||
|
|
6cb1ffbe4e | ||
|
|
dba5ba5767 | ||
|
|
65a7730bf4 | ||
|
|
e17c363381 | ||
|
|
454ffd7bcd | ||
|
|
be75b1557c | ||
|
|
d12d2fb792 | ||
|
|
4debb053bd | ||
|
|
d45a62867f | ||
|
|
32c7e02f09 | ||
|
|
e0ba712631 | ||
|
|
fd17c4ed20 | ||
|
|
76eb06e6b9 | ||
|
|
7a7f169e0a | ||
|
|
61090be973 | ||
|
|
a0360fac17 | ||
|
|
61210acff0 | ||
|
|
eca785a99a | ||
|
|
0b9442d2b8 | ||
|
|
b6a24b0ab2 | ||
|
|
9f43f73350 | ||
|
|
d6ccb0044c | ||
|
|
5d13415770 | ||
|
|
f4c72d5216 | ||
|
|
bb28d796a4 | ||
|
|
f0b078bcd1 | ||
|
|
e926f1b954 | ||
|
|
9f4988e12a | ||
|
|
124c12bebd | ||
|
|
84acde7db0 | ||
|
|
142bf83ffd | ||
|
|
30587f6e8f | ||
|
|
b290474e5d | ||
|
|
de02d2fa6e | ||
|
|
b286aa979f | ||
|
|
c18fc567a3 | ||
|
|
5bd1f4727c | ||
|
|
619d4994da | ||
|
|
1666d6f5ad | ||
|
|
6443aaba3d | ||
|
|
e14619fffc | ||
|
|
07ec53e148 | ||
|
|
1c9d11f967 | ||
|
|
2da1da7c40 | ||
|
|
85b949a7df | ||
|
|
2c0d46278a | ||
|
|
74653968df | ||
|
|
49b55c7763 | ||
|
|
d02b38681c | ||
|
|
d625c8c2b7 | ||
|
|
9f33e8103a | ||
|
|
abd375f501 | ||
|
|
715c37addf | ||
|
|
754ce39076 | ||
|
|
580f98419d | ||
|
|
c8a7948671 | ||
|
|
9da3112c04 | ||
|
|
12ca498386 | ||
|
|
2f9c1857bb | ||
|
|
ac2ee6dfff | ||
|
|
bfde6a4dc8 | ||
|
|
dc93d054a9 | ||
|
|
173366ba7d | ||
|
|
4bfca79860 | ||
|
|
baa3659f20 | ||
|
|
59b2996862 | ||
|
|
d0dc42ea2f | ||
|
|
235c666b61 | ||
|
|
12d6c13f4e | ||
|
|
a7befbe8a6 | ||
|
|
0a31daf6ef | ||
|
|
34b97418b1 | ||
|
|
9baa1c0095 | ||
|
|
af27a2f0ec | ||
|
|
0c7cb4c566 | ||
|
|
bf3846ac30 | ||
|
|
d9db5752c1 | ||
|
|
33b21778d0 | ||
|
|
58916d47b9 | ||
|
|
49a095de44 | ||
|
|
c9f5f0b122 | ||
|
|
942823bb1f | ||
|
|
9cbca40e90 | ||
|
|
fcbcef0472 | ||
|
|
3274631b04 | ||
|
|
310b844c64 | ||
|
|
f50a1ed243 | ||
|
|
8ff7d4ec04 | ||
|
|
0bfa34357e | ||
|
|
ad1ee0a52d | ||
|
|
d9705a3cbe | ||
|
|
3c91f8eb52 | ||
|
|
9d36920115 | ||
|
|
8731006619 | ||
|
|
19cac4a08a | ||
|
|
a9679e56d3 | ||
|
|
8e833d3510 | ||
|
|
f227fedd46 | ||
|
|
804cf1b3fa | ||
|
|
7e23f93ba7 | ||
|
|
1a79fb0176 | ||
|
|
7d9ced0d58 | ||
|
|
a0ea8eca27 | ||
|
|
61193c51c5 | ||
|
|
849f6fb683 | ||
|
|
24bb07e981 | ||
|
|
ad3b55fc0d | ||
|
|
e5bc13e9ed | ||
|
|
2fbd8e059a | ||
|
|
b20c5374b7 | ||
|
|
05ea6ce233 | ||
|
|
72a37aa07a | ||
|
|
ae6028b7f6 | ||
|
|
13b2b5bc96 | ||
|
|
49bd90bc96 | ||
|
|
2d463f7b39 | ||
|
|
24b0301000 | ||
|
|
70c65ca13b | ||
|
|
80b4a29ce5 | ||
|
|
239c32e9c0 | ||
|
|
78b0a46654 | ||
|
|
6597361075 | ||
|
|
ad544ef4fa | ||
|
|
ceb8b2f513 | ||
|
|
b4bb80caab | ||
|
|
c6cfb2dee9 | ||
|
|
2792dd026e | ||
|
|
43567441cc | ||
|
|
3d2751bb2b | ||
|
|
bdd8390913 | ||
|
|
77fea4f81c | ||
|
|
b9403085c6 | ||
|
|
2ad8aa4829 | ||
|
|
eecaeabf45 | ||
|
|
67f92d6b4d | ||
|
|
8fd2cd98c7 | ||
|
|
3d79a60464 | ||
|
|
5c8f0f46d1 | ||
|
|
a58889ce14 | ||
|
|
37d7395f91 | ||
|
|
5039be3db8 | ||
|
|
b64dd14f2f | ||
|
|
dd2af38196 | ||
|
|
77c3d851a2 | ||
|
|
99b15a0d71 | ||
|
|
04e869ce48 | ||
|
|
dc467bcfee | ||
|
|
21aa7d0ce9 | ||
|
|
32a60456e5 | ||
|
|
01719c15c7 | ||
|
|
8a29924a12 | ||
|
|
59eb25a5ad | ||
|
|
538912c89d | ||
|
|
b4cf8482c1 | ||
|
|
de7d466acb | ||
|
|
456ca26817 | ||
|
|
0bf4dd4313 | ||
|
|
f18e383e33 | ||
|
|
5f34e9672b | ||
|
|
9f2ab9ba1b | ||
|
|
d984d3a5f9 | ||
|
|
35b7b034ce | ||
|
|
b5262b0c3a | ||
|
|
a79b397d17 | ||
|
|
46a33851fd | ||
|
|
1da52a3fec | ||
|
|
12065fdb99 | ||
|
|
d5c1daaa48 | ||
|
|
be65f1ce58 | ||
|
|
163c11b60c | ||
|
|
47a280cc16 | ||
|
|
ba34df3b4b | ||
|
|
51473ec137 | ||
|
|
2da61c1e54 | ||
|
|
e23f040962 | ||
|
|
f370e96058 | ||
|
|
ec88f9bd02 | ||
|
|
bce0c8de06 | ||
|
|
44ceccff12 |
No files matched your search
@@ -31,7 +31,7 @@ jobs:
|
||||
run: make servers
|
||||
|
||||
- name: Server Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: servers
|
||||
path: ./sliver-server_*
|
||||
@@ -42,10 +42,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
- name: Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
go-version: "^1.22"
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
run: make clients
|
||||
|
||||
- name: Client Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: clients
|
||||
path: ./sliver-client_*
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- run: mkdir -p ./builds
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./builds
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.21
|
||||
- name: Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
go-version: "^1.22"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
languages: go
|
||||
@@ -48,6 +48,6 @@ jobs:
|
||||
run: make
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
env:
|
||||
VERSION: ${{github.sha}}
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Detect package manager
|
||||
id: detect-package-manager
|
||||
@@ -63,10 +63,10 @@ jobs:
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.next/cache
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
run: ${{ steps.detect-package-manager.outputs.runner }} next build
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/docs/sliver-docs/out
|
||||
|
||||
@@ -99,4 +99,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -23,16 +23,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
- name: Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
go-version: "^1.22"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
@@ -57,10 +56,10 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
- name: Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
go-version: "^1.22"
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
@@ -75,9 +74,6 @@ jobs:
|
||||
run: make macos-amd64
|
||||
|
||||
- name: Test MacOS (amd64)
|
||||
env:
|
||||
SLIVER_CC_32: /usr/local/bin/i686-w64-mingw32-gcc
|
||||
SLIVER_CC_64: /usr/local/bin/x86_64-w64-mingw32-gcc
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
|
||||
- name: Make MacOS (arm64)
|
||||
@@ -88,10 +84,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
- name: Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
go-version: "^1.22"
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
+7
-12
@@ -9,7 +9,7 @@
|
||||
|
||||
# STAGE: base
|
||||
## Compiles Sliver for use
|
||||
FROM --platform=linux/amd64 golang:1.21.4 as base
|
||||
FROM golang:1.22.2 as base
|
||||
|
||||
### Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
@@ -29,12 +29,12 @@ RUN cp -vv sliver-server /opt/sliver-server
|
||||
# STAGE: test
|
||||
## Run unit tests against the compiled instance
|
||||
## Use `--target test` in the docker build command to run this stage
|
||||
FROM --platform=linux/amd64 base as test
|
||||
FROM base as test
|
||||
|
||||
RUN apt-get update --fix-missing \
|
||||
&& apt-get -y upgrade \
|
||||
&& apt-get -y install \
|
||||
curl gcc-multilib build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
curl
|
||||
|
||||
RUN /opt/sliver-server unpack --force
|
||||
|
||||
@@ -43,7 +43,7 @@ RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
|
||||
# STAGE: production
|
||||
## Final dockerized form of Sliver
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim as production
|
||||
FROM debian:bookworm-slim as production
|
||||
|
||||
### Install production packages
|
||||
RUN apt-get update --fix-missing \
|
||||
@@ -56,8 +56,7 @@ RUN apt-get update --fix-missing \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
curl libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
nasm
|
||||
|
||||
### Install MSF for stager generation
|
||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
||||
@@ -92,15 +91,11 @@ ENTRYPOINT [ "/opt/sliver-server" ]
|
||||
|
||||
|
||||
# STAGE: production-slim (about 1Gb smaller)
|
||||
### Slim production image, i.e. without MSF and assoicated libraries
|
||||
### Still include GCC and MinGW for cross-platform generation
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim as production-slim
|
||||
FROM debian:bookworm-slim as production-slim
|
||||
|
||||
### Install production packages
|
||||
RUN apt-get update --fix-missing \
|
||||
&& apt-get -y upgrade \
|
||||
&& apt-get -y install \
|
||||
build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
&& apt-get -y upgrade
|
||||
|
||||
### Cleanup unneeded packages
|
||||
RUN apt-get autoremove -y \
|
||||
|
||||
+58
-19
@@ -27,7 +27,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
armoryConfigFileName = "armories.json"
|
||||
ArmoryConfigFileName = "armories.json"
|
||||
DefaultArmoryName = "Default"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -36,9 +37,11 @@ var (
|
||||
// DefaultArmoryRepoURL - The default repo url for the armory
|
||||
DefaultArmoryRepoURL string
|
||||
|
||||
defaultArmoryConfig = &ArmoryConfig{
|
||||
DefaultArmoryConfig = &ArmoryConfig{
|
||||
PublicKey: DefaultArmoryPublicKey,
|
||||
RepoURL: DefaultArmoryRepoURL,
|
||||
Name: DefaultArmoryName,
|
||||
Enabled: true,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -48,29 +51,65 @@ type ArmoryConfig struct {
|
||||
RepoURL string `json:"repo_url"`
|
||||
Authorization string `json:"authorization"`
|
||||
AuthorizationCmd string `json:"authorization_cmd"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// GetArmoriesConfig - The parsed armory config file
|
||||
func GetArmoriesConfig() []*ArmoryConfig {
|
||||
armoryConfigPath := filepath.Join(GetRootAppDir(), armoryConfigFileName)
|
||||
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
|
||||
return []*ArmoryConfig{defaultArmoryConfig}
|
||||
}
|
||||
data, err := os.ReadFile(armoryConfigPath)
|
||||
if err != nil {
|
||||
return []*ArmoryConfig{defaultArmoryConfig}
|
||||
}
|
||||
var armoryConfigs []*ArmoryConfig
|
||||
err = json.Unmarshal(data, &armoryConfigs)
|
||||
if err != nil {
|
||||
return []*ArmoryConfig{defaultArmoryConfig}
|
||||
}
|
||||
for _, armoryConfig := range armoryConfigs {
|
||||
func RefreshArmoryAuthorization(armories []*ArmoryConfig) {
|
||||
for _, armoryConfig := range armories {
|
||||
if armoryConfig.AuthorizationCmd != "" {
|
||||
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
|
||||
}
|
||||
}
|
||||
return append(armoryConfigs, defaultArmoryConfig)
|
||||
}
|
||||
|
||||
// GetArmoriesConfig - The parsed armory config file
|
||||
func GetArmoriesConfig() []*ArmoryConfig {
|
||||
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName)
|
||||
if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) {
|
||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
||||
}
|
||||
data, err := os.ReadFile(armoryConfigPath)
|
||||
if err != nil {
|
||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
||||
}
|
||||
var armoryConfigsFromFile []*ArmoryConfig
|
||||
var armoryConfigs []*ArmoryConfig
|
||||
err = json.Unmarshal(data, &armoryConfigsFromFile)
|
||||
if err != nil {
|
||||
return []*ArmoryConfig{DefaultArmoryConfig}
|
||||
}
|
||||
|
||||
// Force the default armory to be the last
|
||||
defaultArmorySpecified := false
|
||||
|
||||
for _, config := range armoryConfigsFromFile {
|
||||
if config.Name == DefaultArmoryName {
|
||||
defaultArmorySpecified = true
|
||||
continue
|
||||
} else {
|
||||
armoryConfigs = append(armoryConfigs, config)
|
||||
}
|
||||
}
|
||||
if defaultArmorySpecified {
|
||||
armoryConfigs = append(armoryConfigs, DefaultArmoryConfig)
|
||||
}
|
||||
RefreshArmoryAuthorization(armoryConfigs)
|
||||
|
||||
return armoryConfigs
|
||||
}
|
||||
|
||||
func SaveArmoriesConfig(armories []*ArmoryConfig) error {
|
||||
configData, err := json.Marshal(armories)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
armoryConfigPath := filepath.Join(GetRootAppDir(), ArmoryConfigFileName)
|
||||
err = os.WriteFile(armoryConfigPath, configData, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
|
||||
|
||||
+9
-8
@@ -24,20 +24,17 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
)
|
||||
|
||||
const (
|
||||
logFileName = "sliver-client.log"
|
||||
)
|
||||
|
||||
var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||
|
||||
// Initialize logging
|
||||
// Initialize logging.
|
||||
func initLogging(appDir string) *os.File {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
|
||||
@@ -49,6 +46,10 @@ func initLogging(appDir string) *os.File {
|
||||
}
|
||||
|
||||
func init() {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
rootCmd.TraverseChildren = true
|
||||
|
||||
// Create the console client, without any RPC or commands bound to it yet.
|
||||
@@ -88,10 +89,10 @@ var rootCmd = &cobra.Command{
|
||||
Long: ``,
|
||||
}
|
||||
|
||||
// Execute - Execute root command
|
||||
// Execute - Execute root command.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Printf("root command: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
)
|
||||
|
||||
|
||||
+23
-10
@@ -1,37 +1,50 @@
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/transport"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// consoleCmd generates the console with required pre/post runners
|
||||
func consoleCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
// consoleCmd generates the console with required pre/post runners.
|
||||
func consoleCmd(con *console.SliverClient) *cobra.Command {
|
||||
consoleCmd := &cobra.Command{
|
||||
Use: "console",
|
||||
Short: "Start the sliver client console",
|
||||
}
|
||||
|
||||
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
|
||||
|
||||
return consoleCmd
|
||||
}
|
||||
|
||||
func consoleRunnerCmd(con *console.SliverConsoleClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
var ln *grpc.ClientConn
|
||||
|
||||
pre = func(_ *cobra.Command, _ []string) error {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
|
||||
+26
-9
@@ -1,19 +1,36 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command"
|
||||
"github.com/bishopfox/sliver/client/command/use"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func implantCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
func implantCmd(con *console.SliverClient) *cobra.Command {
|
||||
con.IsCLI = true
|
||||
|
||||
makeCommands := command.SliverCommands(con)
|
||||
@@ -25,7 +42,7 @@ func implantCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
implantFlags.StringP("use", "s", "", "interact with a session")
|
||||
cmd.Flags().AddFlagSet(implantFlags)
|
||||
|
||||
// Prerunners (console setup, connection, etc)
|
||||
// Pre-runners (console setup, connection, etc)
|
||||
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
|
||||
|
||||
// Completions
|
||||
@@ -34,7 +51,7 @@ func implantCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func makeRunners(implantCmd *cobra.Command, con *console.SliverConsoleClient) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
func makeRunners(implantCmd *cobra.Command, con *console.SliverClient) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
startConsole, closeConsole := consoleRunnerCmd(con, false)
|
||||
|
||||
// The pre-run function connects to the server and sets up a "fake" console,
|
||||
@@ -59,7 +76,7 @@ func makeRunners(implantCmd *cobra.Command, con *console.SliverConsoleClient) (p
|
||||
return pre, closeConsole
|
||||
}
|
||||
|
||||
func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
func makeCompleters(cmd *cobra.Command, con *console.SliverClient) {
|
||||
comps := carapace.Gen(cmd)
|
||||
|
||||
comps.PreRun(func(cmd *cobra.Command, args []string) {
|
||||
@@ -67,7 +84,7 @@ func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
})
|
||||
|
||||
// Bind completers to flags (wrap them to use the same pre-runners)
|
||||
command.FlagComps(cmd, func(comp *carapace.ActionMap) {
|
||||
command.BindFlagCompletions(cmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
cmd.PersistentPreRunE(cmd, c.Args)
|
||||
return use.SessionIDCompleter(con)
|
||||
|
||||
@@ -22,10 +22,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
)
|
||||
|
||||
func importCmd() *cobra.Command {
|
||||
|
||||
@@ -21,9 +21,8 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdVersion = &cobra.Command{
|
||||
|
||||
@@ -27,15 +27,14 @@ import (
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AliasesCmd - The alias command
|
||||
func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) error {
|
||||
// AliasesCmd - The alias command.
|
||||
func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) error {
|
||||
if 0 < len(loadedAliases) {
|
||||
PrintAliases(con)
|
||||
} else {
|
||||
@@ -45,8 +44,8 @@ func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintAliases - Print a list of loaded aliases
|
||||
func PrintAliases(con *console.SliverConsoleClient) {
|
||||
// PrintAliases - Print a list of loaded aliases.
|
||||
func PrintAliases(con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
@@ -88,8 +87,8 @@ func PrintAliases(con *console.SliverConsoleClient) {
|
||||
con.Println(tw.Render())
|
||||
}
|
||||
|
||||
// AliasCommandNameCompleter - Completer for installed extensions command names
|
||||
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
// AliasCommandNameCompleter - Completer for installed extensions command names.
|
||||
func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverClient) []string {
|
||||
results := []string{}
|
||||
for name := range loadedAliases {
|
||||
if strings.HasPrefix(name, prefix) {
|
||||
@@ -129,7 +128,7 @@ func getInstalledManifests() map[string]*AliasManifest {
|
||||
return installedManifests
|
||||
}
|
||||
|
||||
// AliasCommandNameCompleter - Completer for installed extensions command names
|
||||
// AliasCommandNameCompleter - Completer for installed extensions command names.
|
||||
func AliasCompleter() carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := []string{}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
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,30 +25,29 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AliasesInstallCmd - Install an alias
|
||||
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// AliasesInstallCmd - Install an alias.
|
||||
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
aliasLocalPath := args[0]
|
||||
fi, err := os.Stat(aliasLocalPath)
|
||||
if os.IsNotExist(err) {
|
||||
con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
|
||||
con.PrintErrorf("Alias path '%s' does not exist", aliasLocalPath)
|
||||
return
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
InstallFromFile(aliasLocalPath, false, con)
|
||||
InstallFromFile(aliasLocalPath, "", false, con)
|
||||
} else {
|
||||
installFromDir(aliasLocalPath, con)
|
||||
}
|
||||
}
|
||||
|
||||
// Install an extension from a directory
|
||||
func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
// Install an extension from a directory.
|
||||
func installFromDir(aliasLocalPath string, con *console.SliverClient) {
|
||||
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
|
||||
@@ -100,8 +99,8 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
con.Printf("done!\n")
|
||||
}
|
||||
|
||||
// Install an extension from a .tar.gz file
|
||||
func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
|
||||
// Install an extension from a .tar.gz file.
|
||||
func InstallFromFile(aliasGzFilePath string, aliasName string, promptToOverwrite bool, con *console.SliverClient) *string {
|
||||
manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, aliasGzFilePath, err)
|
||||
@@ -109,12 +108,18 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
}
|
||||
manifest, err := ParseAliasManifest(manifestData)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
|
||||
errorMsg := ""
|
||||
if aliasName != "" {
|
||||
errorMsg = fmt.Sprintf("Error processing manifest for alias %s - failed to parse %s: %s\n", aliasName, ManifestFileName, err)
|
||||
} else {
|
||||
errorMsg = fmt.Sprintf("Failed to parse %s: %s\n", ManifestFileName, err)
|
||||
}
|
||||
con.PrintErrorf(errorMsg)
|
||||
return nil
|
||||
}
|
||||
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
|
||||
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
|
||||
if !autoOverwrite {
|
||||
if promptToOverwrite {
|
||||
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
@@ -140,7 +145,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
}
|
||||
for _, aliasFile := range manifest.Files {
|
||||
if aliasFile.Path != "" {
|
||||
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
|
||||
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install file: %s\n", err)
|
||||
forceRemoveAll(installPath)
|
||||
@@ -152,7 +157,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
return &installPath
|
||||
}
|
||||
|
||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string) error {
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator))))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -28,11 +28,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
app "github.com/reeflective/console"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -40,6 +35,10 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
app "github.com/reeflective/console"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -53,30 +52,30 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// alias name -> manifest/command
|
||||
// alias name -> manifest/command.
|
||||
loadedAliases = map[string]*loadedAlias{}
|
||||
|
||||
defaultHostProc = map[string]string{
|
||||
"windows": windowsDefaultHostProc,
|
||||
"linux": windowsDefaultHostProc,
|
||||
"linux": linuxDefaultHostProc,
|
||||
"darwin": macosDefaultHostProc,
|
||||
}
|
||||
)
|
||||
|
||||
// Ties the manifest struct to the command struct
|
||||
// Ties the manifest struct to the command struct.
|
||||
type loadedAlias struct {
|
||||
Manifest *AliasManifest
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
// AliasFile - An OS/Arch specific file
|
||||
// AliasFile - An OS/Arch specific file.
|
||||
type AliasFile struct {
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// AliasManifest - The manifest for an alias, contains metadata
|
||||
// AliasManifest - The manifest for an alias, contains metadata.
|
||||
type AliasManifest struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
@@ -93,7 +92,9 @@ type AliasManifest struct {
|
||||
IsReflective bool `json:"is_reflective"`
|
||||
IsAssembly bool `json:"is_assembly"`
|
||||
|
||||
RootPath string `json:"-"`
|
||||
RootPath string `json:"-"`
|
||||
ArmoryName string `json:"-"`
|
||||
ArmoryPK string `json:"-"`
|
||||
}
|
||||
|
||||
func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err error) {
|
||||
@@ -104,7 +105,7 @@ func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err er
|
||||
return
|
||||
}
|
||||
|
||||
func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
|
||||
func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch string) (string, error) {
|
||||
filePath := ""
|
||||
for _, extFile := range a.Files {
|
||||
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
||||
@@ -124,7 +125,7 @@ func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, target
|
||||
}
|
||||
|
||||
// AliasesLoadCmd - Locally load a alias into the Sliver shell.
|
||||
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
alias, err := LoadAlias(dirPath, cmd.Root(), con)
|
||||
@@ -135,8 +136,8 @@ func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAlias - Load an alias into the Sliver shell from a given directory
|
||||
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||
// LoadAlias - Load an alias into the Sliver shell from a given directory.
|
||||
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClient) (*AliasManifest, error) {
|
||||
// retrieve alias manifest
|
||||
var err error
|
||||
manifestPath, err = filepath.Abs(manifestPath)
|
||||
@@ -205,7 +206,7 @@ func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConso
|
||||
return aliasManifest, nil
|
||||
}
|
||||
|
||||
// ParseAliasManifest - Parse an alias manifest
|
||||
// ParseAliasManifest - Parse an alias manifest.
|
||||
func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||
// parse it
|
||||
alias := &AliasManifest{}
|
||||
@@ -241,7 +242,7 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||
return alias, nil
|
||||
}
|
||||
|
||||
func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -268,20 +269,20 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
return
|
||||
}
|
||||
// args := ctx.Args.StringList("arguments")
|
||||
var extArgs string
|
||||
var extArgsStr string
|
||||
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
|
||||
extArgs = aliasManifest.DefaultArgs
|
||||
extArgsStr = aliasManifest.DefaultArgs
|
||||
} else {
|
||||
extArgs = strings.Join(args, " ")
|
||||
extArgsStr = strings.Join(args, " ")
|
||||
}
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
extArgsStr = strings.TrimSpace(extArgsStr)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||
// Special case for payloads with pass to Donut (.NET assemblies and sideloaded payloads):
|
||||
// The Donut loader has a hard limit of 256 characters for the command line arguments, so
|
||||
// we're alerting the user that the arguments will be truncated.
|
||||
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
if len(extArgsStr) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
msgStr := ""
|
||||
// The --in-process flag only exists for .NET assemblies (aliasManifest.IsAssembly == true).
|
||||
// Groupping the two conditions together could crash the client since ctx.Flags.Type panics
|
||||
@@ -353,13 +354,13 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
|
||||
// Execute Assembly
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Arguments: args,
|
||||
Assembly: binData,
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
@@ -399,11 +400,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
|
||||
// Spawn DLL
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: strings.Trim(extArgs, " "),
|
||||
Args: args,
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
EntryPoint: aliasManifest.Entrypoint,
|
||||
@@ -438,11 +439,11 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
|
||||
// Sideload
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgsStr)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: extArgs,
|
||||
Args: args,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
ProcessName: processName,
|
||||
@@ -474,26 +475,26 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSpawnDLLOutput - Prints the output of a spawn dll command
|
||||
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||
// PrintSpawnDLLOutput - Prints the output of a spawn dll command.
|
||||
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(spawnDllResp.GetResult()))
|
||||
outFilePath.WriteString(spawnDllResp.GetResult())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSideloadOutput - Prints the output of a sideload command
|
||||
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||
// PrintSideloadOutput - Prints the output of a sideload command.
|
||||
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(sideloadResp.GetResult()))
|
||||
outFilePath.WriteString(sideloadResp.GetResult())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// PrintAssemblyOutput - Prints the output of an execute-assembly command
|
||||
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||
// PrintAssemblyOutput - Prints the output of an execute-assembly command.
|
||||
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(execAsmResp.GetOutput())
|
||||
|
||||
@@ -25,14 +25,13 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AliasesRemoveCmd - Locally load a alias into the Sliver shell.
|
||||
func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
@@ -54,8 +53,8 @@ func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveAliasByCommandName - Remove an alias by command name
|
||||
func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClient) error {
|
||||
// RemoveAliasByCommandName - Remove an alias by command name.
|
||||
func RemoveAliasByCommandName(commandName string, con *console.SliverClient) error {
|
||||
if commandName == "" {
|
||||
return errors.New("command name is required")
|
||||
}
|
||||
|
||||
+366
-76
@@ -23,6 +23,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -54,13 +55,24 @@ type ArmoryPackage struct {
|
||||
RepoURL string `json:"repo_url"`
|
||||
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
|
||||
type ArmoryBundle struct {
|
||||
Name string `json:"name"`
|
||||
Packages []string `json:"packages"`
|
||||
Name string `json:"name"`
|
||||
Packages []string `json:"packages"`
|
||||
ArmoryName string `json:"-"`
|
||||
}
|
||||
|
||||
// ArmoryHTTPConfig - Configuration for armory HTTP client
|
||||
@@ -89,48 +101,75 @@ type pkgCacheEntry struct {
|
||||
Alias *alias.AliasManifest
|
||||
Extension *extensions.ExtensionManifest
|
||||
LastErr error
|
||||
// This corresponds to Pkg.ID
|
||||
ID string
|
||||
}
|
||||
|
||||
var (
|
||||
// public key -> armoryCacheEntry
|
||||
indexCache = sync.Map{}
|
||||
// public key -> armoryPkgCacheEntry
|
||||
// package ID -> armoryPkgCacheEntry
|
||||
pkgCache = sync.Map{}
|
||||
// public key -> assets.ArmoryConfig
|
||||
currentArmories = sync.Map{}
|
||||
|
||||
// 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
|
||||
defaultTimeout = 15 * time.Minute
|
||||
|
||||
// Track whether armories have been initialized so that we know if we need to pull from the config
|
||||
armoriesInitialized = false
|
||||
|
||||
// Track whether the default armory has been removed by the user (this is needed to prevent it from being readded in if they have removed it)
|
||||
defaultArmoryRemoved = false
|
||||
)
|
||||
|
||||
// ArmoryCmd - The main armory command
|
||||
func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
armoriesConfig := assets.GetArmoriesConfig()
|
||||
con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
|
||||
func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
armoriesConfig := getCurrentArmoryConfiguration()
|
||||
if len(armoriesConfig) == 1 {
|
||||
con.Printf("Reading armory index ... ")
|
||||
} else {
|
||||
con.PrintInfof("Reading %d armory indexes ... ", len(armoriesConfig))
|
||||
}
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||
indexes := fetchIndexes(clientConfig)
|
||||
if len(indexes) != len(armoriesConfig) {
|
||||
con.Printf("errors!\n")
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(indexCacheEntry)
|
||||
if cacheEntry.LastErr != nil {
|
||||
con.PrintErrorf("%s - %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||
con.PrintErrorf("%s (%s) - %s\n", cacheEntry.ArmoryConfig.Name, cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
con.Printf("done!\n")
|
||||
}
|
||||
armoriesInitialized = true
|
||||
if len(indexes) == 0 {
|
||||
con.PrintInfof("No indexes found\n")
|
||||
return
|
||||
}
|
||||
aliases := []*alias.AliasManifest{}
|
||||
exts := []*extensions.ExtensionManifest{}
|
||||
|
||||
if 0 < len(indexes) {
|
||||
con.PrintInfof("Fetching package information ... ")
|
||||
fetchPackageSignatures(indexes, clientConfig)
|
||||
for _, index := range indexes {
|
||||
errorCount := 0
|
||||
aliases := []*alias.AliasManifest{}
|
||||
exts := []*extensions.ExtensionManifest{}
|
||||
con.PrintInfof("Reading package information for armory %s ... ", index.ArmoryConfig.Name)
|
||||
fetchPackageSignatures(index, clientConfig)
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(pkgCacheEntry)
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Something is wrong with this entry
|
||||
pkgCache.Delete(value)
|
||||
return true
|
||||
}
|
||||
if cacheEntry.ArmoryConfig.PublicKey != index.ArmoryConfig.PublicKey {
|
||||
return true
|
||||
}
|
||||
if cacheEntry.LastErr != nil {
|
||||
errorCount++
|
||||
if errorCount == 0 {
|
||||
@@ -149,32 +188,52 @@ func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri
|
||||
if errorCount == 0 {
|
||||
con.Printf("done!\n")
|
||||
}
|
||||
if 0 < len(aliases) || 0 < len(exts) {
|
||||
con.Println()
|
||||
PrintArmoryPackages(aliases, exts, con)
|
||||
} else {
|
||||
con.PrintInfof("No packages found\n")
|
||||
}
|
||||
|
||||
}
|
||||
if 0 < len(aliases) || 0 < len(exts) {
|
||||
con.Println()
|
||||
bundles := bundlesInCache()
|
||||
if 0 < len(bundles) {
|
||||
PrintArmoryBundles(bundles, con)
|
||||
} else {
|
||||
con.PrintInfof("No bundles found\n")
|
||||
}
|
||||
PrintArmoryPackages(aliases, exts, con)
|
||||
} else {
|
||||
con.PrintInfof("No indexes found\n")
|
||||
con.PrintInfof("No packages found\n")
|
||||
}
|
||||
con.Println()
|
||||
bundles := bundlesInCache()
|
||||
if 0 < len(bundles) {
|
||||
PrintArmoryBundles(bundles, con)
|
||||
} else {
|
||||
con.PrintInfof("No bundles found\n")
|
||||
}
|
||||
}
|
||||
|
||||
func refresh(clientConfig ArmoryHTTPConfig) {
|
||||
armoriesConfig := assets.GetArmoriesConfig()
|
||||
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||
fetchPackageSignatures(indexes, clientConfig)
|
||||
getCurrentArmoryConfiguration()
|
||||
indexes := fetchIndexes(clientConfig)
|
||||
for _, index := range indexes {
|
||||
fetchPackageSignatures(index, clientConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
|
||||
func countUniqueCommandsFromManifests(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest) (int, int) {
|
||||
uniqueAliasNames := []string{}
|
||||
uniqueExtensionNames := []string{}
|
||||
|
||||
for _, alias := range aliases {
|
||||
if !slices.Contains(uniqueAliasNames, alias.CommandName) {
|
||||
uniqueAliasNames = append(uniqueAliasNames, alias.CommandName)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ext := range exts {
|
||||
for _, cmd := range ext.ExtCommand {
|
||||
if !slices.Contains(uniqueExtensionNames, cmd.CommandName) {
|
||||
uniqueExtensionNames = append(uniqueExtensionNames, cmd.CommandName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(uniqueAliasNames), len(uniqueExtensionNames)
|
||||
}
|
||||
|
||||
func packageManifestsInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) {
|
||||
aliases := []*alias.AliasManifest{}
|
||||
exts := []*extensions.ExtensionManifest{}
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
@@ -191,6 +250,125 @@ func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest)
|
||||
return aliases, exts
|
||||
}
|
||||
|
||||
func armoryLookupByName(name string) *assets.ArmoryConfig {
|
||||
var result *assets.ArmoryConfig
|
||||
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
indexEntry, ok := value.(indexCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if indexEntry.ArmoryConfig.Name == name {
|
||||
result = indexEntry.ArmoryConfig
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the packages in the cache with a given name
|
||||
func packageCacheLookupByName(name string) []*pkgCacheEntry {
|
||||
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.Pkg.Name == name {
|
||||
result = append(result, &cacheEntry)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the packages in the cache for a given command name
|
||||
func packageCacheLookupByCmd(commandName string) []*pkgCacheEntry {
|
||||
var result []*pkgCacheEntry = make([]*pkgCacheEntry, 0)
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.Pkg.CommandName == commandName {
|
||||
result = append(result, &cacheEntry)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the package in the cache for a given command name and armory
|
||||
func packageCacheLookupByCmdAndArmory(commandName string, armoryPublicKey string) *pkgCacheEntry {
|
||||
var result *pkgCacheEntry
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey && cacheEntry.Pkg.CommandName == commandName {
|
||||
result = &cacheEntry
|
||||
// Stop iterating
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns the package hashes in the cache for a given armory
|
||||
func packageHashLookupByArmory(armoryPublicKey string) []string {
|
||||
result := []string{}
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.ArmoryConfig.PublicKey == armoryPublicKey {
|
||||
result = append(result, cacheEntry.ID)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func packageCacheLookupByID(packageID string) *pkgCacheEntry {
|
||||
var packageEntry *pkgCacheEntry
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry, ok := value.(pkgCacheEntry)
|
||||
if !ok {
|
||||
// Keep going
|
||||
return true
|
||||
}
|
||||
if cacheEntry.LastErr == nil {
|
||||
if cacheEntry.ID == packageID {
|
||||
packageEntry = &cacheEntry
|
||||
// Stop iterating
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return packageEntry
|
||||
}
|
||||
|
||||
func bundlesInCache() []*ArmoryBundle {
|
||||
bundles := []*ArmoryBundle{}
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
@@ -207,7 +385,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
|
||||
var action carapace.Action
|
||||
|
||||
results := []string{}
|
||||
aliases, exts := packagesInCache()
|
||||
aliases, exts := packageManifestsInCache()
|
||||
bundles := bundlesInCache()
|
||||
|
||||
for _, aliasPkg := range aliases {
|
||||
@@ -242,7 +420,7 @@ func AliasExtensionOrBundleCompleter() carapace.Action {
|
||||
}
|
||||
|
||||
// PrintArmoryPackages - Prints the armory packages
|
||||
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) {
|
||||
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverClient) {
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
width = 1
|
||||
@@ -256,6 +434,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
|
||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Armory",
|
||||
"Command Name",
|
||||
"Version",
|
||||
"Type",
|
||||
@@ -264,6 +443,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Armory",
|
||||
"Command Name",
|
||||
"Version",
|
||||
"Type",
|
||||
@@ -273,10 +453,11 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
|
||||
// Columns start at 1 for some dumb reason
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Number: 1, Mode: table.Asc},
|
||||
{Number: 2, Mode: table.Asc},
|
||||
})
|
||||
|
||||
type pkgInfo struct {
|
||||
ArmoryName string
|
||||
CommandName string
|
||||
Version string
|
||||
Type string
|
||||
@@ -284,8 +465,10 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
URL string
|
||||
}
|
||||
entries := []pkgInfo{}
|
||||
|
||||
for _, aliasPkg := range aliases {
|
||||
entries = append(entries, pkgInfo{
|
||||
ArmoryName: aliasPkg.ArmoryName,
|
||||
CommandName: aliasPkg.CommandName,
|
||||
Version: aliasPkg.Version,
|
||||
Type: "Alias",
|
||||
@@ -296,6 +479,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
for _, extm := range exts {
|
||||
for _, extension := range extm.ExtCommand {
|
||||
entries = append(entries, pkgInfo{
|
||||
ArmoryName: extm.ArmoryName,
|
||||
CommandName: extension.CommandName,
|
||||
Version: extension.Manifest.Version,
|
||||
Type: "Extension",
|
||||
@@ -315,6 +499,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
}
|
||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||
rows = append(rows, table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||
@@ -323,6 +508,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
})
|
||||
} else {
|
||||
rows = append(rows, table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.ArmoryName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||
@@ -335,11 +521,12 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
}
|
||||
|
||||
// PrintArmoryBundles - Prints the armory bundles
|
||||
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClient) {
|
||||
func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + "Bundles" + console.Normal)
|
||||
tw.AppendHeader(table.Row{
|
||||
"Armory Name",
|
||||
"Name",
|
||||
"Contains",
|
||||
})
|
||||
@@ -365,6 +552,7 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClien
|
||||
}
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
bundle.ArmoryName,
|
||||
bundle.Name,
|
||||
packages,
|
||||
})
|
||||
@@ -402,12 +590,19 @@ func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig {
|
||||
|
||||
// fetch armory indexes, only returns indexes that were fetched successfully
|
||||
// errors are still in the cache objects however and can be checked
|
||||
func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) []ArmoryIndex {
|
||||
func fetchIndexes(clientConfig ArmoryHTTPConfig) []ArmoryIndex {
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, armoryConfig := range armoryConfigs {
|
||||
wg.Add(1)
|
||||
go fetchIndex(armoryConfig, clientConfig, wg)
|
||||
}
|
||||
// Try to get a max of 10 indexes at a time
|
||||
currentRequests := make(chan struct{}, 10)
|
||||
currentArmories.Range(func(key, value interface{}) bool {
|
||||
armoryEntry := value.(assets.ArmoryConfig)
|
||||
if armoryEntry.Enabled {
|
||||
wg.Add(1)
|
||||
currentRequests <- struct{}{}
|
||||
go fetchIndex(&armoryEntry, currentRequests, clientConfig, wg)
|
||||
}
|
||||
return true
|
||||
})
|
||||
wg.Wait()
|
||||
indexes := []ArmoryIndex{}
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
@@ -420,13 +615,19 @@ func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPC
|
||||
return indexes
|
||||
}
|
||||
|
||||
func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
|
||||
func fetchIndex(armoryConfig *assets.ArmoryConfig, requestChannel chan struct{}, clientConfig ArmoryHTTPConfig, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
<-requestChannel
|
||||
}()
|
||||
cacheEntry, ok := indexCache.Load(armoryConfig.PublicKey)
|
||||
if ok {
|
||||
cached := cacheEntry.(indexCacheEntry)
|
||||
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
||||
return
|
||||
} else if time.Since(cached.Fetched) >= cacheTime {
|
||||
// If an index has gone stale, remove it from the index cache
|
||||
indexCache.Delete(armoryConfig.PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,40 +664,95 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig
|
||||
}
|
||||
}
|
||||
|
||||
func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig) {
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, index := range indexes {
|
||||
for _, armoryPkg := range index.Extensions {
|
||||
wg.Add(1)
|
||||
armoryPkg.IsAlias = false
|
||||
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
|
||||
}
|
||||
for _, armoryPkg := range index.Aliases {
|
||||
wg.Add(1)
|
||||
armoryPkg.IsAlias = true
|
||||
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
|
||||
}
|
||||
func calculateHashesForIndex(index ArmoryIndex) []string {
|
||||
result := []string{}
|
||||
|
||||
for _, pkg := range index.Aliases {
|
||||
result = append(result, calculatePackageHash(pkg))
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, pkg := range index.Extensions {
|
||||
result = append(result, calculatePackageHash(pkg))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
|
||||
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()
|
||||
|
||||
// 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) {
|
||||
defer wg.Done()
|
||||
cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
|
||||
defer func() {
|
||||
<-requestChannel
|
||||
}()
|
||||
cacheEntry, ok := pkgCache.Load(armoryPkg.ID)
|
||||
if ok {
|
||||
cached := cacheEntry.(pkgCacheEntry)
|
||||
if time.Since(cached.Fetched) < cacheTime && cached.LastErr == nil && !clientConfig.IgnoreCache {
|
||||
return
|
||||
} else if time.Since(cached.Fetched) >= cacheTime {
|
||||
// If a package has gone stale, remove it from the package cache
|
||||
pkgCache.Delete(armoryPkg.ID)
|
||||
}
|
||||
}
|
||||
|
||||
pkgCacheEntry := &pkgCacheEntry{
|
||||
ArmoryConfig: armoryConfig,
|
||||
RepoURL: armoryPkg.RepoURL,
|
||||
ID: armoryPkg.ID,
|
||||
}
|
||||
defer func() {
|
||||
pkgCacheEntry.Fetched = time.Now()
|
||||
pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
|
||||
pkgCache.Store(armoryPkg.ID, *pkgCacheEntry)
|
||||
}()
|
||||
|
||||
repoURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
@@ -528,19 +784,53 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig
|
||||
if armoryPkg != nil {
|
||||
pkgCacheEntry.Pkg = *armoryPkg
|
||||
}
|
||||
if err == nil {
|
||||
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
|
||||
return
|
||||
}
|
||||
if armoryPkg.IsAlias {
|
||||
pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
|
||||
} else {
|
||||
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
|
||||
}
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
|
||||
}
|
||||
|
||||
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
|
||||
return
|
||||
}
|
||||
if armoryPkg.IsAlias {
|
||||
pkgCacheEntry.Alias, err = alias.ParseAliasManifest(manifestData)
|
||||
pkgCacheEntry.Alias.ArmoryName = armoryConfig.Name
|
||||
pkgCacheEntry.Alias.ArmoryPK = armoryConfig.PublicKey
|
||||
} else {
|
||||
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
|
||||
pkgCacheEntry.Extension.ArmoryName = armoryConfig.Name
|
||||
pkgCacheEntry.Extension.ArmoryPK = armoryConfig.PublicKey
|
||||
}
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func clearAllCaches() {
|
||||
currentArmories.Range(func(key, value any) bool {
|
||||
currentArmories.Delete(key)
|
||||
return true
|
||||
})
|
||||
indexCache.Range(func(key, value any) bool {
|
||||
indexCache.Delete(key)
|
||||
return true
|
||||
})
|
||||
pkgCache.Range(func(key, value any) bool {
|
||||
pkgCache.Delete(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func getArmoryPublicKey(armoryName string) string {
|
||||
// Find PK for the armory name
|
||||
armoryPK := ""
|
||||
currentArmories.Range(func(key, value any) bool {
|
||||
armoryEntry := value.(assets.ArmoryConfig)
|
||||
if armoryEntry.Name == armoryName {
|
||||
armoryPK = armoryEntry.PublicKey
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return armoryPK
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
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,122 +19,444 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
// ErrPackageNotFound - The package was not found
|
||||
var ErrPackageNotFound = errors.New("package not found")
|
||||
var ErrPackageAlreadyInstalled = errors.New("package is already installed")
|
||||
|
||||
const (
|
||||
doNotInstallOption = "Do not install this package"
|
||||
doNotInstallPackageName = "do not install"
|
||||
)
|
||||
|
||||
// ArmoryInstallCmd - The armory install command
|
||||
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var promptToOverwrite bool
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("A package or bundle name is required")
|
||||
return
|
||||
}
|
||||
forceInstallation, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not parse %q flag: %s\n", "force", err)
|
||||
return
|
||||
}
|
||||
if forceInstallation {
|
||||
promptToOverwrite = false
|
||||
} else {
|
||||
promptToOverwrite = true
|
||||
}
|
||||
|
||||
armoryName, err := cmd.Flags().GetString("armory")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Find PK for the armory name
|
||||
armoryPK := getArmoryPublicKey(armoryName)
|
||||
|
||||
// If the armory with the name is not found, print a warning
|
||||
if cmd.Flags().Changed("armory") && armoryPK == "" {
|
||||
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
|
||||
}
|
||||
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
if name == "all" {
|
||||
aliases, extensions := packagesInCache()
|
||||
aliases, extensions := packageManifestsInCache()
|
||||
aliasCount, extCount := countUniqueCommandsFromManifests(aliases, extensions)
|
||||
confirm := false
|
||||
pluralAliases := "es"
|
||||
if aliasCount == 1 {
|
||||
pluralAliases = ""
|
||||
}
|
||||
pluralExtensions := "s"
|
||||
if extCount == 1 {
|
||||
pluralExtensions = ""
|
||||
}
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Install %d aliases and %d extensions?",
|
||||
len(aliases), len(extensions),
|
||||
Message: fmt.Sprintf("Install %d alias%s and %d extension%s?",
|
||||
aliasCount, pluralAliases, extCount, pluralExtensions,
|
||||
),
|
||||
}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
promptToOverwrite = false
|
||||
}
|
||||
err := installPackageByName(name, clientConfig, con)
|
||||
err = installPackageByName(name, armoryPK, forceInstallation, promptToOverwrite, clientConfig, con)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == ErrPackageNotFound {
|
||||
if errors.Is(err, ErrPackageNotFound) {
|
||||
bundles := bundlesInCache()
|
||||
for _, bundle := range bundles {
|
||||
if bundle.Name == name {
|
||||
installBundle(bundle, clientConfig, con)
|
||||
installBundle(bundle, armoryPK, forceInstallation, clientConfig, con)
|
||||
return
|
||||
}
|
||||
}
|
||||
// If we have made it here, then there was not a bundle or package that matched the provided name
|
||||
if armoryPK == "" {
|
||||
con.PrintErrorf("No package or bundle named %q was found\n", name)
|
||||
} else {
|
||||
con.PrintErrorf("No package or bundle named %q was found in armory %s\n", name, armoryName)
|
||||
}
|
||||
} else if errors.Is(err, ErrPackageAlreadyInstalled) {
|
||||
con.PrintErrorf("Package %q is already installed - use the force option to overwrite it\n", name)
|
||||
} else {
|
||||
con.PrintErrorf("Could not install package: %s\n", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func installBundle(bundle *ArmoryBundle, armoryPK string, forceInstallation bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) {
|
||||
installList := []string{}
|
||||
pendingPackages := make(map[string]string)
|
||||
|
||||
for _, bundlePkgName := range bundle.Packages {
|
||||
packageInstallList, err := buildInstallList(bundlePkgName, armoryPK, forceInstallation, pendingPackages)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrPackageAlreadyInstalled) {
|
||||
con.PrintInfof("Package %s is already installed. Skipping...\n", bundlePkgName)
|
||||
continue
|
||||
} else {
|
||||
con.PrintErrorf("Error for package %s: %s\n", bundlePkgName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, pkgID := range packageInstallList {
|
||||
if !slices.Contains(installList, pkgID) {
|
||||
installList = append(installList, pkgID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, packageID := range installList {
|
||||
packageEntry := packageCacheLookupByID(packageID)
|
||||
if packageEntry == nil {
|
||||
con.PrintErrorf("The package cache is out of date. Please run armory refresh and try again.\n")
|
||||
return
|
||||
}
|
||||
if packageEntry.Pkg.IsAlias {
|
||||
err := installAliasPackage(packageEntry, false, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install alias '%s': %s", packageEntry.Alias.CommandName, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err := installExtensionPackage(packageEntry, false, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install extension '%s': %s", packageEntry.Extension.Name, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
con.PrintErrorf("No package or bundle named '%s' was found", name)
|
||||
}
|
||||
|
||||
func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||
for _, pkgName := range bundle.Packages {
|
||||
err := installPackageByName(pkgName, clientConfig, con)
|
||||
func getInstalledPackageNames() []string {
|
||||
packageNames := []string{}
|
||||
|
||||
installedAliases := assets.GetInstalledAliasManifests()
|
||||
installedExtensions := assets.GetInstalledExtensionManifests()
|
||||
|
||||
for _, aliasFileName := range installedAliases {
|
||||
alias := &alias.AliasManifest{}
|
||||
manifestData, err := os.ReadFile(aliasFileName)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install '%s': %s", pkgName, err)
|
||||
continue
|
||||
}
|
||||
err = json.Unmarshal(manifestData, alias)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(packageNames, alias.CommandName) {
|
||||
packageNames = append(packageNames, alias.CommandName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
|
||||
aliases, extensions := packagesInCache()
|
||||
for _, alias := range aliases {
|
||||
if alias.CommandName == name || name == "all" {
|
||||
installAlias(alias, clientConfig, con)
|
||||
if name != "all" {
|
||||
return nil
|
||||
}
|
||||
for _, extensionFileName := range installedExtensions {
|
||||
extension := &extensions.ExtensionManifest{}
|
||||
manifestData, err := os.ReadFile(extensionFileName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, extm := range extensions {
|
||||
for _, ext := range extm.ExtCommand {
|
||||
if ext.CommandName == name || name == "all" {
|
||||
installExtension(ext.Manifest, clientConfig, con)
|
||||
if name != "all" {
|
||||
return nil
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if name == "all" {
|
||||
con.Printf("\n")
|
||||
con.PrintInfof("All packages installed\n")
|
||||
return nil
|
||||
}
|
||||
return ErrPackageNotFound
|
||||
|
||||
return packageNames
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
// This is a convenience function to get the names of the commands in the cache
|
||||
func getCommandsInCache() []string {
|
||||
commandNames := []string{}
|
||||
|
||||
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
|
||||
if cacheEntry.LastErr == nil {
|
||||
if cacheEntry.Pkg.IsAlias {
|
||||
if !slices.Contains(commandNames, cacheEntry.Alias.CommandName) {
|
||||
commandNames = append(commandNames, cacheEntry.Alias.CommandName)
|
||||
}
|
||||
} else {
|
||||
for _, command := range cacheEntry.Extension.ExtCommand {
|
||||
if !slices.Contains(commandNames, command.CommandName) {
|
||||
commandNames = append(commandNames, command.CommandName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return commandNames
|
||||
}
|
||||
|
||||
func getPackagesWithCommandName(name, armoryPK, minimumVersion string) []*pkgCacheEntry {
|
||||
packages := []*pkgCacheEntry{}
|
||||
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(pkgCacheEntry)
|
||||
if cacheEntry.LastErr == nil {
|
||||
if cacheEntry.Pkg.IsAlias {
|
||||
if cacheEntry.Alias.CommandName == name {
|
||||
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Alias.Version >= minimumVersion) {
|
||||
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
|
||||
packages = append(packages, &cacheEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, command := range cacheEntry.Extension.ExtCommand {
|
||||
if command.CommandName == name {
|
||||
if minimumVersion == "" || (minimumVersion != "" && cacheEntry.Extension.Version >= minimumVersion) {
|
||||
if armoryPK == "" || (armoryPK != "" && cacheEntry.ArmoryConfig.PublicKey == armoryPK) {
|
||||
packages = append(packages, &cacheEntry)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return packages
|
||||
}
|
||||
|
||||
func getPackageIDFromUser(name string, options map[string]string) string {
|
||||
selectedPackageKey := ""
|
||||
optionKeys := util.Keys(options)
|
||||
slices.Sort(optionKeys)
|
||||
// Add a cancel option
|
||||
optionKeys = append(optionKeys, doNotInstallOption)
|
||||
options[doNotInstallOption] = doNotInstallPackageName
|
||||
prompt := &survey.Select{
|
||||
Message: fmt.Sprintf("More than one package contains the command %s. Please choose an option from the list below:", name),
|
||||
Options: optionKeys,
|
||||
}
|
||||
survey.AskOne(prompt, &selectedPackageKey)
|
||||
selectedPackageID := options[selectedPackageKey]
|
||||
|
||||
return selectedPackageID
|
||||
}
|
||||
|
||||
func getPackageForCommand(name, armoryPK, minimumVersion string) (*pkgCacheEntry, error) {
|
||||
packagesWithCommand := getPackagesWithCommandName(name, armoryPK, minimumVersion)
|
||||
|
||||
if len(packagesWithCommand) > 1 {
|
||||
// Build an option map for the user to choose from (option -> pkgID)
|
||||
optionMap := make(map[string]string)
|
||||
for _, packageEntry := range packagesWithCommand {
|
||||
var optionName string
|
||||
if packageEntry.Pkg.IsAlias {
|
||||
optionName = fmt.Sprintf("Alias %s %s from armory %s (%s)",
|
||||
name,
|
||||
packageEntry.Alias.Version,
|
||||
packageEntry.ArmoryConfig.Name,
|
||||
packageEntry.Pkg.RepoURL,
|
||||
)
|
||||
} else {
|
||||
optionName = fmt.Sprintf("Extension %s %s from armory %s with command %s (%s)",
|
||||
packageEntry.Pkg.Name,
|
||||
packageEntry.Extension.Version,
|
||||
packageEntry.ArmoryConfig.Name,
|
||||
name,
|
||||
packageEntry.Pkg.RepoURL,
|
||||
)
|
||||
}
|
||||
optionMap[optionName] = packageEntry.ID
|
||||
}
|
||||
selectedPackageID := getPackageIDFromUser(name, optionMap)
|
||||
if selectedPackageID == doNotInstallPackageName {
|
||||
return nil, fmt.Errorf("user cancelled installation")
|
||||
}
|
||||
for _, packageEntry := range packagesWithCommand {
|
||||
if packageEntry.ID == selectedPackageID {
|
||||
return packageEntry, nil
|
||||
}
|
||||
}
|
||||
} else if len(packagesWithCommand) == 1 {
|
||||
return packagesWithCommand[0], nil
|
||||
}
|
||||
return nil, ErrPackageNotFound
|
||||
}
|
||||
|
||||
func buildInstallList(name, armoryPK string, forceInstallation bool, pendingPackages map[string]string) ([]string, error) {
|
||||
packageInstallList := []string{}
|
||||
installedPackages := getInstalledPackageNames()
|
||||
|
||||
/*
|
||||
Gather information about what we are working with
|
||||
|
||||
Find all conflicts within aliases for a given name (or all names), same thing with extensions
|
||||
Then if there are aliases and extensions with a given name, make sure to note that for when we ask the user what to do
|
||||
*/
|
||||
var requestedPackageList []string
|
||||
if name == "all" {
|
||||
requestedPackageList = []string{}
|
||||
allCommands := getCommandsInCache()
|
||||
for _, cmdName := range allCommands {
|
||||
if !slices.Contains(installedPackages, cmdName) || forceInstallation {
|
||||
// Check to see if there is a package pending with that name
|
||||
if _, ok := pendingPackages[cmdName]; !ok {
|
||||
requestedPackageList = append(requestedPackageList, cmdName)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !slices.Contains(installedPackages, name) || forceInstallation {
|
||||
// Check to see if there is a package pending with that name
|
||||
if _, ok := pendingPackages[name]; !ok {
|
||||
requestedPackageList = []string{name}
|
||||
}
|
||||
} else {
|
||||
return nil, ErrPackageAlreadyInstalled
|
||||
}
|
||||
}
|
||||
|
||||
for _, packageName := range requestedPackageList {
|
||||
if _, ok := pendingPackages[packageName]; ok {
|
||||
// We are already going to install a package with this name, so do not try to resolve it
|
||||
continue
|
||||
}
|
||||
packageEntry, err := getPackageForCommand(packageName, armoryPK, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !slices.Contains(packageInstallList, packageEntry.ID) {
|
||||
packageInstallList = append(packageInstallList, packageEntry.ID)
|
||||
pendingPackages[packageName] = packageEntry.ID
|
||||
}
|
||||
|
||||
if !packageEntry.Pkg.IsAlias {
|
||||
dependencies := make(map[string]*pkgCacheEntry)
|
||||
err = resolveExtensionPackageDependencies(packageEntry, dependencies, pendingPackages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for pkgName, packageEntry := range dependencies {
|
||||
if !slices.Contains(packageInstallList, packageEntry.ID) {
|
||||
packageInstallList = append(packageInstallList, packageEntry.ID)
|
||||
}
|
||||
if _, ok := pendingPackages[pkgName]; !ok {
|
||||
pendingPackages[pkgName] = packageEntry.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packageInstallList, nil
|
||||
}
|
||||
|
||||
func installPackageByName(name, armoryPK string, forceInstallation, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
pendingPackages := make(map[string]string)
|
||||
packageInstallList, err := buildInstallList(name, armoryPK, forceInstallation, pendingPackages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(packageInstallList) > 0 {
|
||||
for _, packageID := range packageInstallList {
|
||||
entry := packageCacheLookupByID(packageID)
|
||||
if entry == nil {
|
||||
return errors.New("cache consistency error - please refresh the cache and try again")
|
||||
}
|
||||
if entry.Pkg.IsAlias {
|
||||
err := installAliasPackage(entry, promptToOverwrite, clientConfig, con)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install alias '%s': %s", entry.Alias.CommandName, err)
|
||||
}
|
||||
} else {
|
||||
err := installExtensionPackage(entry, promptToOverwrite, clientConfig, con)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install extension '%s': %s", entry.Extension.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ErrPackageNotFound
|
||||
}
|
||||
|
||||
if name == "all" {
|
||||
con.Printf("\n")
|
||||
con.PrintInfof("Operation complete\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func installAliasPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
if entry == nil {
|
||||
return errors.New("package not found")
|
||||
}
|
||||
if !entry.Pkg.IsAlias {
|
||||
return errors.New("package is not an alias")
|
||||
}
|
||||
repoURL, err := url.Parse(entry.RepoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -174,7 +496,7 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
|
||||
con.Printf(console.Clearln + "\r") // Clear the line
|
||||
|
||||
installPath := alias.InstallFromFile(tmpFile.Name(), true, con)
|
||||
installPath := alias.InstallFromFile(tmpFile.Name(), entry.Alias.CommandName, promptToOverwrite, con)
|
||||
if installPath == nil {
|
||||
return errors.New("failed to install alias")
|
||||
}
|
||||
@@ -188,50 +510,15 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
return nil
|
||||
}
|
||||
|
||||
func installExtension(extm *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||
deps := make(map[string]struct{})
|
||||
for _, ext := range extm.ExtCommand {
|
||||
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
|
||||
sliverMenu := con.App.Menu(constants.ImplantMenu).Root()
|
||||
for dep := range deps {
|
||||
if extensions.CmdExists(dep, sliverMenu) {
|
||||
continue // Dependency is already installed
|
||||
}
|
||||
err := installExtensionPackageByName(dep, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install extension dependency '%s': %s", dep, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
err := installExtensionPackageByName(ext.CommandName, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install extension '%s': %s", ext.CommandName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const maxDepDepth = 10 // Arbitrary recursive limit for dependencies
|
||||
|
||||
func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||
var entry *pkgCacheEntry
|
||||
pkgCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(pkgCacheEntry)
|
||||
if cacheEntry.Pkg.CommandName == name {
|
||||
entry = &cacheEntry
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if entry == nil {
|
||||
return
|
||||
}
|
||||
for _, multiExt := range entry.Extension.ExtCommand {
|
||||
func resolveExtensionPackageDependencies(pkg *pkgCacheEntry, deps map[string]*pkgCacheEntry, pendingPackages map[string]string) error {
|
||||
for _, multiExt := range pkg.Extension.ExtCommand {
|
||||
if multiExt.DependsOn == "" {
|
||||
continue // Avoid adding empty dependency
|
||||
}
|
||||
|
||||
if multiExt.DependsOn == name {
|
||||
if multiExt.DependsOn == pkg.Extension.Name {
|
||||
continue // Avoid infinite loop of something that depends on itself
|
||||
}
|
||||
// We also need to look out for circular dependencies, so if we've already
|
||||
@@ -239,24 +526,28 @@ func resolveExtensionPackageDependencies(name string, deps map[string]struct{},
|
||||
if _, ok := deps[multiExt.DependsOn]; ok {
|
||||
continue // Already resolved
|
||||
}
|
||||
// Check to make sure we are not already going to install a package with this name
|
||||
if _, ok := pendingPackages[multiExt.DependsOn]; ok {
|
||||
continue
|
||||
}
|
||||
if maxDepDepth < len(deps) {
|
||||
continue
|
||||
}
|
||||
deps[multiExt.DependsOn] = struct{}{}
|
||||
resolveExtensionPackageDependencies(multiExt.DependsOn, deps, clientConfig, con)
|
||||
// Figure out what package we need for the dependency
|
||||
dependencyEntry, err := getPackageForCommand(multiExt.DependsOn, "", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not resolve dependency %s for %s: %s", multiExt.DependsOn, pkg.Extension.Name, err)
|
||||
}
|
||||
deps[multiExt.DependsOn] = dependencyEntry
|
||||
err = resolveExtensionPackageDependencies(dependencyEntry, deps, pendingPackages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
func installExtensionPackage(entry *pkgCacheEntry, promptToOverwrite bool, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error {
|
||||
if entry == nil {
|
||||
return errors.New("package not found")
|
||||
}
|
||||
@@ -302,7 +593,7 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
|
||||
con.Printf(console.Clearln + "\r") // Clear download message
|
||||
|
||||
extensions.InstallFromDir(tmpFile.Name(), con, true)
|
||||
extensions.InstallFromDir(tmpFile.Name(), promptToOverwrite, con, true)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,653 @@
|
||||
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,6 +19,8 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -67,6 +69,18 @@ type armoryPkgResponse struct {
|
||||
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
|
||||
}
|
||||
|
||||
func calculatePackageHash(pkg *ArmoryPackage) string {
|
||||
if pkg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
hasher := sha256.New()
|
||||
// Hash some of the things that make the package unique
|
||||
packageIdentifier := []byte(pkg.RepoURL + pkg.PublicKey + pkg.ArmoryName + pkg.CommandName)
|
||||
hasher.Write(packageIdentifier)
|
||||
return fmt.Sprintf("%x", hasher.Sum(nil))
|
||||
}
|
||||
|
||||
//
|
||||
// Default Parsers for Self-Hosted Armories
|
||||
//
|
||||
@@ -79,7 +93,10 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, body, _ := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("api returned non-200 status code")
|
||||
}
|
||||
@@ -111,9 +128,45 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Populate armory name and ID information for assets
|
||||
for _, bundle := range armoryIndex.Bundles {
|
||||
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
}
|
||||
for _, alias := range armoryIndex.Aliases {
|
||||
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
if cached := packageCacheLookupByCmdAndArmory(alias.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
|
||||
// A package with this name is in the cache, so we will remove it here so that the latest version is added
|
||||
// when the index is parsed
|
||||
pkgCache.Delete(cached.ID)
|
||||
}
|
||||
alias.ID = calculatePackageHash(alias)
|
||||
}
|
||||
for _, extension := range armoryIndex.Extensions {
|
||||
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
if cached := packageCacheLookupByCmdAndArmory(extension.CommandName, armoryIndex.ArmoryConfig.PublicKey); cached != nil {
|
||||
// A package with this name is in the cache, so we will remove it here so that the latest version is added
|
||||
// when the index is parsed
|
||||
pkgCache.Delete(cached.ID)
|
||||
}
|
||||
extension.ID = calculatePackageHash(extension)
|
||||
}
|
||||
return armoryIndex, nil
|
||||
}
|
||||
|
||||
func decodePackageSignature(sigString string) ([]byte, error) {
|
||||
// Base64 decode the signature
|
||||
decodedData := make([]byte, base64.StdEncoding.DecodedLen(len(sigString)))
|
||||
_, err := base64.StdEncoding.Decode(decodedData, []byte(sigString))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If decodedData does not end up being base64.StdEncoding.DecodedLen(len(data)) bytes long, trim the null bytes
|
||||
decodedData = bytes.Trim(decodedData, "\x00")
|
||||
return decodedData, nil
|
||||
}
|
||||
|
||||
// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url
|
||||
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
var publicKey minisign.PublicKey
|
||||
@@ -122,25 +175,29 @@ func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *Armory
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, body, err := httpRequest(clientConfig, armoryConfig.RepoURL, armoryConfig, http.Header{})
|
||||
resp, body, err := httpRequest(clientConfig, armoryPkg.RepoURL, armoryConfig, http.Header{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, nil, errors.New("api returned non-200 status code")
|
||||
return nil, nil, fmt.Errorf("api returned non-200 status code (%d)", resp.StatusCode)
|
||||
}
|
||||
pkgResp := &armoryPkgResponse{}
|
||||
err = json.Unmarshal(body, pkgResp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sig, err := parsePkgMinsig([]byte(pkgResp.Minisig))
|
||||
decodedSig, err := decodePackageSignature(pkgResp.Minisig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sig, err := parsePkgMinsig(decodedSig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var tarGz []byte
|
||||
if !sigOnly {
|
||||
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
tarGzURL, err := url.Parse(pkgResp.TarGzURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -242,6 +299,20 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Populate armory name and ID information for assets
|
||||
for _, bundle := range armoryIndex.Bundles {
|
||||
bundle.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
}
|
||||
for _, alias := range armoryIndex.Aliases {
|
||||
alias.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
alias.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
alias.ID = calculatePackageHash(alias)
|
||||
}
|
||||
for _, extension := range armoryIndex.Extensions {
|
||||
extension.ArmoryName = armoryIndex.ArmoryConfig.Name
|
||||
extension.ArmoryPK = armoryIndex.ArmoryConfig.PublicKey
|
||||
extension.ID = calculatePackageHash(extension)
|
||||
}
|
||||
return armoryIndex, nil
|
||||
}
|
||||
|
||||
@@ -436,6 +507,12 @@ func downloadRequest(clientConfig ArmoryHTTPConfig, reqURL string, armoryConfig
|
||||
"Accept": {"application/octet-stream"},
|
||||
}
|
||||
resp, body, err := httpRequest(clientConfig, reqURL, armoryConfig, downloadHdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("invalid response when downloading %s, try again later", reqURL)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||
return nil, fmt.Errorf("error downloading asset: http %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
)
|
||||
|
||||
// ArmorySearchCmd - Search for packages by name
|
||||
func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
con.PrintInfof("Refreshing package cache ... ")
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
@@ -47,7 +47,7 @@ func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
return
|
||||
}
|
||||
|
||||
aliases, exts := packagesInCache()
|
||||
aliases, exts := packageManifestsInCache()
|
||||
matchedAliases := []*alias.AliasManifest{}
|
||||
for _, a := range aliases {
|
||||
if nameExpr.MatchString(a.CommandName) {
|
||||
|
||||
+297
-37
@@ -19,56 +19,127 @@ package armory
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
)
|
||||
|
||||
type VersionInformation struct {
|
||||
OldVersion string
|
||||
NewVersion string
|
||||
ArmoryName string
|
||||
}
|
||||
|
||||
type PackageType uint
|
||||
|
||||
const (
|
||||
AliasPackage PackageType = iota
|
||||
ExtensionPackage
|
||||
)
|
||||
|
||||
type UpdateIdentifier struct {
|
||||
Type PackageType
|
||||
Name string
|
||||
}
|
||||
|
||||
// ArmoryUpdateCmd - Update all installed extensions/aliases
|
||||
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var selectedUpdates []UpdateIdentifier
|
||||
var err error
|
||||
|
||||
con.PrintInfof("Refreshing package cache ... ")
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
con.Printf(console.Clearln + "\r")
|
||||
|
||||
// Aliases
|
||||
aliasUpdates := checkForAliasUpdates(clientConfig, con)
|
||||
if 0 < len(aliasUpdates) {
|
||||
con.PrintInfof("%d alias(es) out of date: %s\n", len(aliasUpdates), strings.Join(aliasUpdates, ", "))
|
||||
for _, aliasName := range aliasUpdates {
|
||||
err := installAliasPackageByName(aliasName, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to update %s: %s\n", aliasName, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
con.PrintInfof("All aliases up to date!\n")
|
||||
armoryName, err := cmd.Flags().GetString("armory")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not parse %q flag: %s\n", "armory", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Extensions
|
||||
extUpdates := checkForExtensionUpdates(clientConfig, con)
|
||||
if 0 < len(extUpdates) {
|
||||
con.PrintInfof("%d extension(s) out of date: %s\n", len(extUpdates), strings.Join(extUpdates, ", "))
|
||||
for _, extName := range extUpdates {
|
||||
err := installExtensionPackageByName(extName, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to update %s: %s\n", extName, err)
|
||||
}
|
||||
// Find PK for the armory name
|
||||
armoryPK := getArmoryPublicKey(armoryName)
|
||||
|
||||
// If the armory with the name is not found, print a warning
|
||||
if cmd.Flags().Changed("armory") && armoryPK == "" {
|
||||
con.PrintWarnf("Could not find a configured armory named %q - searching all configured armories\n\n", armoryName)
|
||||
}
|
||||
|
||||
// Check packages for updates
|
||||
aliasUpdates := checkForAliasUpdates(armoryPK)
|
||||
extUpdates := checkForExtensionUpdates(armoryPK)
|
||||
|
||||
// Display a table of results
|
||||
if len(aliasUpdates) > 0 || len(extUpdates) > 0 {
|
||||
updateKeys := sortUpdateIdentifiers(aliasUpdates, extUpdates)
|
||||
displayAvailableUpdates(con, updateKeys, aliasUpdates, extUpdates)
|
||||
selectedUpdates, err = getUpdatesFromUser(con, updateKeys)
|
||||
if err != nil {
|
||||
con.PrintErrorf(err.Error() + "\n")
|
||||
return
|
||||
}
|
||||
if len(selectedUpdates) == 0 {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
con.PrintInfof("All extensions up to date!\n")
|
||||
con.PrintSuccessf("All packages are up to date")
|
||||
return
|
||||
}
|
||||
|
||||
for _, update := range selectedUpdates {
|
||||
switch update.Type {
|
||||
case AliasPackage:
|
||||
aliasVersionInfo, ok := aliasUpdates[update.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
updatedPackage, err := getPackageForCommand(update.Name, armoryPK, aliasVersionInfo.NewVersion)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not get update package for alias %s: %s\n", update.Name, err)
|
||||
// Keep going because other packages might not encounter errors
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
|
||||
cachedAliases, _ := packagesInCache()
|
||||
results := []string{}
|
||||
func checkForAliasUpdates(armoryPK string) map[string]VersionInformation {
|
||||
cachedAliases, _ := packageManifestsInCache()
|
||||
results := make(map[string]VersionInformation)
|
||||
for _, aliasManifestPath := range assets.GetInstalledAliasManifests() {
|
||||
data, err := os.ReadFile(aliasManifestPath)
|
||||
if err != nil {
|
||||
@@ -79,19 +150,31 @@ func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverCons
|
||||
continue
|
||||
}
|
||||
for _, latestAlias := range cachedAliases {
|
||||
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
|
||||
// the armory differs at all from the local version, the extension is out of date.
|
||||
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version != localManifest.Version {
|
||||
results = append(results, localManifest.CommandName)
|
||||
/*
|
||||
We used to check if the version identifiers were different between the installed version and the
|
||||
version in the armory. This worked when we had one armory, but with multiple potential armories,
|
||||
we have to have some type of versioning. We do not have an official versioning scheme for packages,
|
||||
so we will rely on the author of the package incrementing the version identifier somehow to determine
|
||||
if a package is newer.
|
||||
*/
|
||||
if latestAlias.CommandName == localManifest.CommandName && latestAlias.Version > localManifest.Version {
|
||||
if latestAlias.ArmoryPK == armoryPK || armoryPK == "" {
|
||||
results[localManifest.CommandName] = VersionInformation{
|
||||
OldVersion: localManifest.Version,
|
||||
NewVersion: latestAlias.Version,
|
||||
ArmoryName: latestAlias.ArmoryName,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string {
|
||||
_, cachedExtensions := packagesInCache()
|
||||
results := []string{}
|
||||
func checkForExtensionUpdates(armoryPK string) map[string]VersionInformation {
|
||||
_, cachedExtensions := packageManifestsInCache()
|
||||
// Return a map of extension name to minimum version to install
|
||||
results := make(map[string]VersionInformation)
|
||||
for _, extManifestPath := range assets.GetInstalledExtensionManifests() {
|
||||
data, err := os.ReadFile(extManifestPath)
|
||||
if err != nil {
|
||||
@@ -102,13 +185,190 @@ func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.Sliver
|
||||
continue
|
||||
}
|
||||
for _, latestExt := range cachedExtensions {
|
||||
// Right now we don't try to enforce any kind of versioning, it is assumed if the version from
|
||||
// the armory differs at all from the local version, the extension is out of date.
|
||||
if latestExt.Name == localManifest.Name && latestExt.Version != localManifest.Version {
|
||||
results = append(results, localManifest.Name)
|
||||
/*
|
||||
We used to check if the version identifiers were different between the installed version and the
|
||||
version in the armory. This worked when we had one armory, but with multiple potential armories,
|
||||
we have to have some type of versioning. We do not have an official versioning scheme for packages,
|
||||
so we will rely on the author of the package incrementing the version identifier somehow to determine
|
||||
if a package is newer.
|
||||
*/
|
||||
if latestExt.Name == localManifest.Name && latestExt.Version > localManifest.Version {
|
||||
if latestExt.ArmoryPK == armoryPK || armoryPK == "" {
|
||||
results[localManifest.Name] = VersionInformation{
|
||||
OldVersion: localManifest.Version,
|
||||
NewVersion: latestExt.Version,
|
||||
ArmoryName: latestExt.ArmoryName,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,14 +21,13 @@ package backdoor
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// BackdoorCmd - Command to inject implant code into an existing binary
|
||||
func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// BackdoorCmd - Command to inject implant code into an existing binary.
|
||||
func BackdoorCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
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
|
||||
func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
killFlag, _ := cmd.Flags().GetString("kill")
|
||||
killAll, _ := cmd.Flags().GetBool("kill-all")
|
||||
|
||||
@@ -94,7 +94,7 @@ func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
|
||||
}
|
||||
|
||||
// PrintBeacons - Display a list of beacons
|
||||
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
|
||||
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) {
|
||||
if len(beacons) == 0 {
|
||||
con.PrintInfof("No beacons 🙁\n")
|
||||
return
|
||||
@@ -103,7 +103,7 @@ func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) table.Writer {
|
||||
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) table.Writer {
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
@@ -112,20 +112,45 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
wideTermWidth := con.Settings.SmallTermWidth < width
|
||||
windowsBeaconInList := false
|
||||
for _, beacon := range beacons {
|
||||
if beacon.OS == "windows" {
|
||||
windowsBeaconInList = true
|
||||
}
|
||||
}
|
||||
if wideTermWidth {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Tasks",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Locale",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
if windowsBeaconInList {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Tasks",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Process (PID)",
|
||||
"Integrity",
|
||||
"Operating System",
|
||||
"Locale",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Tasks",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Process (PID)",
|
||||
"Operating System",
|
||||
"Locale",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
@@ -145,6 +170,9 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
||||
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
|
||||
color = console.Green
|
||||
}
|
||||
if beacon.Integrity == "" {
|
||||
beacon.Integrity = "-"
|
||||
}
|
||||
|
||||
// We need a slice of strings so we can apply filters
|
||||
var rowEntries []string
|
||||
@@ -158,11 +186,19 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||
fmt.Sprintf(color+"%s (%d)"+console.Normal, beacon.Filename, beacon.PID),
|
||||
}
|
||||
|
||||
if windowsBeaconInList {
|
||||
rowEntries = append(rowEntries, fmt.Sprintf(color+"%s"+console.Normal, beacon.Integrity))
|
||||
}
|
||||
|
||||
rowEntries = append(rowEntries, []string{
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Locale),
|
||||
con.FormatDateDelta(time.Unix(beacon.LastCheckin, 0), wideTermWidth, false),
|
||||
con.FormatDateDelta(time.Unix(beacon.NextCheckin, 0), wideTermWidth, true),
|
||||
}
|
||||
}...)
|
||||
} else {
|
||||
rowEntries = []string{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
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
|
||||
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
|
||||
func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) {
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
@@ -102,7 +102,7 @@ func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
|
||||
func GetBeacon(con *console.SliverClient, beaconID string) (*clientpb.Beacon, error) {
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
@@ -120,7 +120,7 @@ func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Bea
|
||||
return nil, ErrBeaconNotFound
|
||||
}
|
||||
|
||||
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
|
||||
func GetBeacons(con *console.SliverClient) (*clientpb.Beacons, error) {
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
)
|
||||
|
||||
// BeaconsPruneCmd - Prune stale beacons automatically
|
||||
func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
duration, _ := cmd.Flags().GetString("duration")
|
||||
pruneDuration, err := time.ParseDuration(duration)
|
||||
if err != nil {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
// BeaconsRmCmd - Display/interact with beacons
|
||||
func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
beacon, err := SelectBeacon(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
// BeaconsWatchCmd - Watch your beacons in real-ish time
|
||||
func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
done := waitForInput()
|
||||
defer func() {
|
||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||
|
||||
@@ -23,17 +23,16 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// BuildersCmd - List external builders
|
||||
func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// BuildersCmd - List external builders.
|
||||
func BuildersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
@@ -46,7 +45,7 @@ func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
|
||||
}
|
||||
}
|
||||
|
||||
func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverConsoleClient) {
|
||||
func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
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}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
)
|
||||
|
||||
// C2ProfileCmd list available http profiles
|
||||
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if profileName == constants.DefaultC2Profile {
|
||||
@@ -61,7 +61,7 @@ func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s
|
||||
PrintC2Profiles(profile, con)
|
||||
}
|
||||
|
||||
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
protocols := []string{constants.HttpStr, constants.HttpsStr}
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
if profileName == "" {
|
||||
@@ -123,6 +123,169 @@ func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar
|
||||
}
|
||||
}
|
||||
|
||||
func ExportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
|
||||
filepath, _ := cmd.Flags().GetString("file")
|
||||
if filepath == "" {
|
||||
con.PrintErrorf("Missing file path\n")
|
||||
return
|
||||
}
|
||||
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
if profileName == "" {
|
||||
con.PrintErrorf("Invalid c2 profile name\n")
|
||||
return
|
||||
}
|
||||
|
||||
if profileName == constants.DefaultC2Profile {
|
||||
httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("failed to fetch HTTP C2 profiles: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if len(httpC2Profiles.Configs) != 1 {
|
||||
profileName = selectC2Profile(httpC2Profiles.Configs)
|
||||
}
|
||||
}
|
||||
|
||||
profile, err := con.Rpc.GetHTTPC2ProfileByName(context.Background(), &clientpb.C2ProfileReq{Name: profileName})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonProfile, err := C2ConfigToJSON(profileName, profile)
|
||||
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)
|
||||
}
|
||||
|
||||
// convert protobuf to json
|
||||
func C2ConfigToJSON(profileName string, profile *clientpb.HTTPC2Config) ([]byte, error) {
|
||||
implantConfig := assets.HTTPC2ImplantConfig{
|
||||
UserAgent: profile.ImplantConfig.UserAgent,
|
||||
ChromeBaseVersion: int(profile.ImplantConfig.ChromeBaseVersion),
|
||||
MacOSVersion: profile.ImplantConfig.MacOSVersion,
|
||||
NonceQueryArgChars: profile.ImplantConfig.NonceQueryArgChars,
|
||||
MaxFiles: int(profile.ImplantConfig.MaxFiles),
|
||||
MinFiles: int(profile.ImplantConfig.MinFiles),
|
||||
MaxPaths: int(profile.ImplantConfig.MaxPaths),
|
||||
MinPaths: int(profile.ImplantConfig.MinFiles),
|
||||
StagerFileExt: profile.ImplantConfig.StagerFileExtension,
|
||||
PollFileExt: profile.ImplantConfig.PollFileExtension,
|
||||
StartSessionFileExt: profile.ImplantConfig.StartSessionFileExtension,
|
||||
SessionFileExt: profile.ImplantConfig.SessionFileExtension,
|
||||
CloseFileExt: profile.ImplantConfig.CloseFileExtension,
|
||||
}
|
||||
|
||||
var headers []assets.NameValueProbability
|
||||
for _, header := range profile.ImplantConfig.Headers {
|
||||
headers = append(headers, assets.NameValueProbability{
|
||||
Name: header.Name,
|
||||
Value: header.Value,
|
||||
Probability: int(header.Probability),
|
||||
})
|
||||
}
|
||||
implantConfig.Headers = headers
|
||||
|
||||
var urlParameters []assets.NameValueProbability
|
||||
for _, urlParameter := range profile.ImplantConfig.ExtraURLParameters {
|
||||
urlParameters = append(urlParameters, assets.NameValueProbability{
|
||||
Name: urlParameter.Name,
|
||||
Value: urlParameter.Value,
|
||||
Probability: int(urlParameter.Probability),
|
||||
})
|
||||
}
|
||||
implantConfig.URLParameters = urlParameters
|
||||
|
||||
var (
|
||||
stagerFiles []string
|
||||
pollFiles []string
|
||||
sessionFiles []string
|
||||
closeFiles []string
|
||||
stagerPaths []string
|
||||
pollPaths []string
|
||||
sessionPaths []string
|
||||
closePaths []string
|
||||
)
|
||||
|
||||
for _, pathSegment := range profile.ImplantConfig.PathSegments {
|
||||
if pathSegment.IsFile {
|
||||
switch pathSegment.SegmentType {
|
||||
case 0:
|
||||
pollFiles = append(pollFiles, pathSegment.Value)
|
||||
case 1:
|
||||
sessionFiles = append(sessionFiles, pathSegment.Value)
|
||||
case 2:
|
||||
closeFiles = append(closeFiles, pathSegment.Value)
|
||||
case 3:
|
||||
stagerFiles = append(stagerFiles, pathSegment.Value)
|
||||
}
|
||||
} else {
|
||||
switch pathSegment.SegmentType {
|
||||
case 0:
|
||||
pollPaths = append(pollPaths, pathSegment.Value)
|
||||
case 1:
|
||||
sessionPaths = append(sessionPaths, pathSegment.Value)
|
||||
case 2:
|
||||
closePaths = append(closePaths, pathSegment.Value)
|
||||
case 3:
|
||||
stagerPaths = append(stagerPaths, pathSegment.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implantConfig.PollFiles = pollFiles
|
||||
implantConfig.SessionFiles = sessionFiles
|
||||
implantConfig.CloseFiles = closeFiles
|
||||
implantConfig.StagerFiles = stagerFiles
|
||||
implantConfig.PollPaths = pollPaths
|
||||
implantConfig.SessionPaths = sessionPaths
|
||||
implantConfig.ClosePaths = closePaths
|
||||
implantConfig.StagerPaths = stagerPaths
|
||||
|
||||
var serverHeaders []assets.NameValueProbability
|
||||
for _, header := range profile.ServerConfig.Headers {
|
||||
serverHeaders = append(serverHeaders, assets.NameValueProbability{
|
||||
Name: header.Name,
|
||||
Value: header.Value,
|
||||
Probability: int(header.Probability),
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
jsonConfig, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return jsonConfig, nil
|
||||
}
|
||||
|
||||
// convert json to protobuf
|
||||
func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *clientpb.HTTPC2Config {
|
||||
|
||||
@@ -248,7 +411,7 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client
|
||||
}
|
||||
|
||||
// PrintImplantBuilds - Print the implant builds on the server
|
||||
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverConsoleClient) {
|
||||
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient) {
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
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")
|
||||
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
C2ProfileCmd.AddCommand(importC2ProfileCmd)
|
||||
C2ProfileCmd.AddCommand(exportC2ProfileCmd)
|
||||
|
||||
return []*cobra.Command{
|
||||
C2ProfileCmd,
|
||||
}
|
||||
}
|
||||
+69
-12
@@ -21,19 +21,18 @@ package command
|
||||
import (
|
||||
"strings"
|
||||
|
||||
client "github.com/bishopfox/sliver/client/console"
|
||||
"github.com/reeflective/console"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const defaultTimeout = 60
|
||||
|
||||
// Flags is a convenience function to bind flags to a given command.
|
||||
// Bind is a convenience function to bind flags to a given command.
|
||||
// name - The name of the flag set (can be empty).
|
||||
// cmd - The command to which the flags should be bound.
|
||||
// flags - A function exposing the flag set through which flags are declared.
|
||||
func Flags(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) {
|
||||
func Bind(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) {
|
||||
flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set.
|
||||
flags(flagSet) // Let the user bind any number of flags to it.
|
||||
|
||||
@@ -44,22 +43,21 @@ func Flags(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag
|
||||
}
|
||||
}
|
||||
|
||||
// FlagComps is a convenience function for adding completions to a command's flags.
|
||||
// BindFlagCompletions is a convenience function for adding completions to a command's flags.
|
||||
// cmd - The command owning the flags to complete.
|
||||
// bind - A function exposing a map["flag-name"]carapace.Action.
|
||||
func FlagComps(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) {
|
||||
func BindFlagCompletions(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) {
|
||||
comps := make(carapace.ActionMap)
|
||||
bind(&comps)
|
||||
|
||||
carapace.Gen(cmd).FlagCompletion(comps)
|
||||
}
|
||||
|
||||
// hideCommand generates a cobra annotation map with a single
|
||||
// console.CommandHiddenFilter key, which value is a comma-separated list
|
||||
// of filters to use in order to expose/hide commands based on requirements.
|
||||
// Ex: cmd.Annotations = hideCommand("windows") will hide the cmd
|
||||
// if the target session/beacon is not a Windows host.
|
||||
func hideCommand(filters ...string) map[string]string {
|
||||
// RestrictTargets generates a cobra annotation map with a single console.CommandHiddenFilter key
|
||||
// to a comma-separated list of filters to use in order to expose/hide commands based on requirements.
|
||||
// Ex: cmd.Annotations = RestrictTargets("windows") will only show the command if the target is Windows.
|
||||
// Ex: cmd.Annotations = RestrictTargets("windows", "beacon") show the command if target is a beacon on Windows.
|
||||
func RestrictTargets(filters ...string) map[string]string {
|
||||
if len(filters) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -76,3 +74,62 @@ func hideCommand(filters ...string) map[string]string {
|
||||
console.CommandFilterKey: filts,
|
||||
}
|
||||
}
|
||||
|
||||
// makeBind returns a commandBinder helper function
|
||||
// @menu - The command menu to which the commands should be bound (either server or implant menu).
|
||||
func makeBind(cmd *cobra.Command, con *client.SliverClient) func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) {
|
||||
return func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) {
|
||||
found := false
|
||||
|
||||
// Ensure the given command group is available in the menu.
|
||||
if group != "" {
|
||||
for _, grp := range cmd.Groups() {
|
||||
if grp.Title == group {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
cmd.AddGroup(&cobra.Group{
|
||||
ID: group,
|
||||
Title: group,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the command to the root
|
||||
for _, command := range cmds {
|
||||
cmd.AddCommand(command(con)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// commandBinder is a helper used to bind commands to a given menu, for a given "command help group".
|
||||
//
|
||||
// @group - Name of the group under which the command should be shown. Preferably use a string in the constants package.
|
||||
// @ cmds - A list of functions returning a list of root commands to bind. See any package's `commands.go` file and function.
|
||||
// type commandBinder func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command)
|
||||
|
||||
// [ Core ]
|
||||
// [ Sessions ]
|
||||
// [ Execution ]
|
||||
// [ Filesystem ]
|
||||
// [ Info ]
|
||||
// [ Network (C2)]
|
||||
// [ Network tools ]
|
||||
// [ Payloads ]
|
||||
// [ Privileges ]
|
||||
// [ Processes ]
|
||||
// [ Aliases ]
|
||||
// [ Extensions ]
|
||||
|
||||
// Commands not to bind in CLI:
|
||||
// - portforwarders
|
||||
// - Socks (and wg-socks ?)
|
||||
// - shell ?
|
||||
|
||||
// Take care of:
|
||||
// - double bind help command
|
||||
// - double bind session commands
|
||||
// - don't bind readline command in CLI.
|
||||
@@ -55,3 +55,32 @@ func ClientInterfacesCompleter() carapace.Action {
|
||||
return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':')
|
||||
})
|
||||
}
|
||||
|
||||
// LocalProxyCompleter gives URL completion to all flags/arguments that accept a client proxy address.
|
||||
func LocalProxyCompleter() carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
prefix := ""
|
||||
|
||||
hostPort := carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action {
|
||||
switch len(c.Parts) {
|
||||
case 0:
|
||||
return ClientInterfacesCompleter()
|
||||
case 1:
|
||||
return carapace.ActionMessage("server port")
|
||||
default:
|
||||
return carapace.ActionValues()
|
||||
}
|
||||
})
|
||||
|
||||
return carapace.ActionMultiParts("://", func(c carapace.Context) carapace.Action {
|
||||
switch len(c.Parts) {
|
||||
case 0:
|
||||
return carapace.ActionValues("http", "https").Tag("proxy protocols").Suffix("://")
|
||||
case 1:
|
||||
return hostPort
|
||||
default:
|
||||
return carapace.ActionValues()
|
||||
}
|
||||
}).Invoke(c).Prefix(prefix).ToA()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
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,18 +25,17 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CrackWordlistsCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackWordlistsCmd - Manage GPU cracking stations.
|
||||
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -55,8 +54,8 @@ func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg
|
||||
}
|
||||
}
|
||||
|
||||
// CrackRulesCmd - Manage GPU cracking stations
|
||||
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackRulesCmd - Manage GPU cracking stations.
|
||||
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -75,8 +74,8 @@ func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []
|
||||
}
|
||||
}
|
||||
|
||||
// CrackHcstat2Cmd - Manage GPU cracking stations
|
||||
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackHcstat2Cmd - Manage GPU cracking stations.
|
||||
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -95,7 +94,7 @@ func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
|
||||
func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{"Name", "Size"})
|
||||
@@ -105,7 +104,7 @@ func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverConsole
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) {
|
||||
func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverClient) {
|
||||
wordlistTable := table.NewWriter()
|
||||
wordlistTable.SetTitle(console.Bold + "Wordlists" + console.Normal)
|
||||
wordlistTable.SetStyle(settings.GetTableStyle(con))
|
||||
@@ -162,8 +161,8 @@ func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverC
|
||||
)
|
||||
}
|
||||
|
||||
// CrackWordlistsAddCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackWordlistsAddCmd - Manage GPU cracking stations.
|
||||
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
@@ -206,8 +205,8 @@ func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient,
|
||||
addCrackFile(wordlist, crackFile, con)
|
||||
}
|
||||
|
||||
// CrackRulesAddCmd - add a rules file
|
||||
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackRulesAddCmd - add a rules file.
|
||||
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
@@ -250,8 +249,8 @@ func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
addCrackFile(rules, crackFile, con)
|
||||
}
|
||||
|
||||
// CrackHcstat2AddCmd - add a hcstat2 file
|
||||
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackHcstat2AddCmd - add a hcstat2 file.
|
||||
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
@@ -294,7 +293,7 @@ func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar
|
||||
addCrackFile(hcstat2, crackFile, con)
|
||||
}
|
||||
|
||||
func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverConsoleClient) {
|
||||
func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverClient) {
|
||||
digest := sha256.New()
|
||||
wordlistReader := io.TeeReader(localFile, digest)
|
||||
|
||||
@@ -402,8 +401,8 @@ func readChunkAt(tmpFile *os.File, offset int64, chunkSize int64) ([]byte, error
|
||||
return chunkBuf[:n], nil
|
||||
}
|
||||
|
||||
// CrackWordlistsRmCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackWordlistsRmCmd - Manage GPU cracking stations.
|
||||
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var wordlistName string
|
||||
if len(args) > 0 {
|
||||
wordlistName = args[0]
|
||||
@@ -436,8 +435,8 @@ func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a
|
||||
}
|
||||
}
|
||||
|
||||
// CrackRulesRmCmd - Manage GPU cracking stations
|
||||
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackRulesRmCmd - Manage GPU cracking stations.
|
||||
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var rulesName string
|
||||
if len(args) > 0 {
|
||||
rulesName = args[0]
|
||||
@@ -470,8 +469,8 @@ func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
// CrackHcstat2RmCmd - remove a hcstat2 file
|
||||
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CrackHcstat2RmCmd - remove a hcstat2 file.
|
||||
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var hcstat2Name string
|
||||
if len(args) > 0 {
|
||||
hcstat2Name = args[0]
|
||||
|
||||
@@ -33,7 +33,7 @@ import (
|
||||
)
|
||||
|
||||
// CrackCmd - GPU password cracking interface
|
||||
func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
if !AreCrackersOnline(con) {
|
||||
PrintNoCrackstations(con)
|
||||
} else {
|
||||
@@ -58,7 +58,7 @@ func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin
|
||||
}
|
||||
|
||||
// CrackStationsCmd - Manage GPU cracking stations
|
||||
func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func CrackStationsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -71,11 +71,11 @@ func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
func PrintNoCrackstations(con *console.SliverConsoleClient) {
|
||||
func PrintNoCrackstations(con *console.SliverClient) {
|
||||
con.PrintInfof("No crackstations connected to server\n")
|
||||
}
|
||||
|
||||
func AreCrackersOnline(con *console.SliverConsoleClient) bool {
|
||||
func AreCrackersOnline(con *console.SliverClient) bool {
|
||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -83,7 +83,7 @@ func AreCrackersOnline(con *console.SliverConsoleClient) bool {
|
||||
return len(crackers.Crackstations) > 0
|
||||
}
|
||||
|
||||
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverConsoleClient) {
|
||||
func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient) {
|
||||
sort.Slice(crackers, func(i, j int) bool {
|
||||
return crackers[i].Name < crackers[j].Name
|
||||
})
|
||||
@@ -96,7 +96,7 @@ func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverConsole
|
||||
}
|
||||
}
|
||||
|
||||
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverConsoleClient) {
|
||||
func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n")
|
||||
@@ -135,7 +135,7 @@ func printCracker(cracker *clientpb.Crackstation, index int, con *console.Sliver
|
||||
printBenchmarks(cracker, con)
|
||||
}
|
||||
|
||||
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverConsoleClient) {
|
||||
func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + "Benchmarks" + console.Normal)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/rsteube/carapace"
|
||||
)
|
||||
|
||||
func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action {
|
||||
func CrackHcstat2Completer(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
@@ -33,7 +33,7 @@ func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action {
|
||||
})
|
||||
}
|
||||
|
||||
func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
func CrackWordlistCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
@@ -57,7 +57,7 @@ func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
})
|
||||
}
|
||||
|
||||
func CrackRulesCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
func CrackRulesCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
|
||||
@@ -37,8 +37,8 @@ const (
|
||||
CSVFormat = "csv" // username,hash\n
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CredsCmd - Add new credentials.
|
||||
func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
collection, _ := cmd.Flags().GetString("collection")
|
||||
username, _ := cmd.Flags().GetString("username")
|
||||
plaintext, _ := cmd.Flags().GetString("plaintext")
|
||||
@@ -76,8 +76,8 @@ func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CredsCmd - Add new credentials.
|
||||
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
collection, _ := cmd.Flags().GetString("collection")
|
||||
filePath := args[0]
|
||||
fileFormat, _ := cmd.Flags().GetString("file-format")
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
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,18 +23,17 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CredsCmd - Manage credentials
|
||||
func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CredsCmd - Manage credentials.
|
||||
func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -47,7 +46,7 @@ func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient) {
|
||||
func PrintCreds(creds []*clientpb.Credential, con *console.SliverClient) {
|
||||
collections := make(map[string][]*clientpb.Credential)
|
||||
for _, cred := range creds {
|
||||
collections[cred.Collection] = append(collections[cred.Collection], cred)
|
||||
@@ -58,7 +57,7 @@ func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient)
|
||||
}
|
||||
}
|
||||
|
||||
func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverConsoleClient) {
|
||||
func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
if collection != "" {
|
||||
@@ -88,7 +87,7 @@ func printCollection(collection string, creds []*clientpb.Credential, con *conso
|
||||
}
|
||||
|
||||
// CredsHashTypeCompleter completes hash types.
|
||||
func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
func CredsHashTypeCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
@@ -101,8 +100,8 @@ func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
})
|
||||
}
|
||||
|
||||
// CredsHashFileFormatCompleter completes file formats for hash-files
|
||||
func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
// CredsHashFileFormatCompleter completes file formats for hash-files.
|
||||
func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionValuesDescribed(
|
||||
UserColonHashNewlineFormat, "One hash per line.",
|
||||
HashNewlineFormat, "A file containing lines of 'username:hash' pairs.",
|
||||
@@ -110,8 +109,8 @@ func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Act
|
||||
).Tag("hash file formats")
|
||||
}
|
||||
|
||||
// CredsCollectionCompleter completes existing creds collection names
|
||||
func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
// CredsCollectionCompleter completes existing creds collection names.
|
||||
func CredsCollectionCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
@@ -134,7 +133,7 @@ func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action
|
||||
}
|
||||
|
||||
// CredsCredentialIDCompleter completes credential IDs.
|
||||
func CredsCredentialIDCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
func CredsCredentialIDCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CredsCmd - Add new credentials.
|
||||
func CredsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var id string
|
||||
if len(args) > 0 {
|
||||
id = args[0]
|
||||
|
||||
@@ -9,14 +9,13 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
|
||||
// SelectCredential - Interactive menu for the user to select a credentials from the database
|
||||
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverConsoleClient) (*clientpb.Credential, error) {
|
||||
// SelectCredential - Interactive menu for the user to select a credentials from the database.
|
||||
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverClient) (*clientpb.Credential, error) {
|
||||
var creds *clientpb.Credentials
|
||||
var err error
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
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,8 +29,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
@@ -38,6 +36,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -51,8 +50,8 @@ var (
|
||||
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory.
|
||||
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
@@ -112,7 +111,7 @@ func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess {
|
||||
chromeProcess, err := getChromeProcess(session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -154,7 +153,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons
|
||||
return curse
|
||||
}
|
||||
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) {
|
||||
name := "Chrome"
|
||||
if isEdge {
|
||||
name = "Edge"
|
||||
@@ -254,7 +253,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) {
|
||||
userDataFlag, _ := cmd.Flags().GetString("user-data")
|
||||
if userDataFlag != "" {
|
||||
return userDataFlag, nil
|
||||
@@ -307,7 +306,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Co
|
||||
}
|
||||
}
|
||||
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) {
|
||||
exeFlag, _ := cmd.Flags().GetString("exe")
|
||||
if exeFlag != "" {
|
||||
return exeFlag, nil
|
||||
@@ -417,7 +416,7 @@ func isChromeProcess(executable string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
|
||||
@@ -27,15 +27,14 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/reeflective/readline"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/reeflective/readline"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
@@ -56,7 +55,7 @@ func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
startCursedConsole(curse, true, target, con)
|
||||
}
|
||||
|
||||
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget {
|
||||
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverClient) *overlord.ChromeDebugTarget {
|
||||
if len(targets) < 1 {
|
||||
con.PrintErrorf("No debug targets\n")
|
||||
return nil
|
||||
@@ -94,7 +93,7 @@ var helperHooks = []string{
|
||||
"console.log = (...a) => {return a;}", // console.log
|
||||
}
|
||||
|
||||
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
|
||||
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverClient) {
|
||||
tmpFile, _ := os.CreateTemp("", "cursed")
|
||||
shell := readline.NewShell()
|
||||
shell.History.AddFromFile("cursed history", tmpFile.Name())
|
||||
|
||||
@@ -24,13 +24,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
|
||||
@@ -21,22 +21,20 @@ package cursed
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory.
|
||||
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
@@ -96,7 +94,7 @@ func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess {
|
||||
edgeProcess, err := getEdgeProcess(session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -153,7 +151,7 @@ func isEdgeProcess(executable string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
|
||||
@@ -27,8 +27,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
@@ -36,9 +34,10 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func CursedElectronCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
@@ -68,7 +67,7 @@ func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg
|
||||
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
|
||||
}
|
||||
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess {
|
||||
exists, err := checkElectronPath(electronExe, session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
@@ -119,7 +118,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co
|
||||
return curse
|
||||
}
|
||||
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (bool, error) {
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (bool, error) {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: electronExe,
|
||||
@@ -130,7 +129,7 @@ func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra
|
||||
return ls.GetExists(), nil
|
||||
}
|
||||
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
@@ -148,7 +147,7 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *co
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) {
|
||||
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
|
||||
debugPort := getRemoteDebuggerPort(cmd)
|
||||
args := []string{
|
||||
|
||||
@@ -23,14 +23,13 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func CursedRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func CursedRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
|
||||
@@ -23,13 +23,12 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
|
||||
@@ -27,16 +27,15 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory.
|
||||
func CursedCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
// Collect existing curses from core
|
||||
cursedProcesses := [][]string{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
@@ -71,8 +70,8 @@ func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri
|
||||
}
|
||||
}
|
||||
|
||||
// selectCursedProcess - Interactively select a cursed process from a list
|
||||
func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
// selectCursedProcess - Interactively select a cursed process from a list.
|
||||
func selectCursedProcess(con *console.SliverClient) *core.CursedProcess {
|
||||
cursedProcesses := []*core.CursedProcess{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess))
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
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
|
||||
|
||||
// DllHijackCmd -- implements the dllhijack command
|
||||
func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func DllHijackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var (
|
||||
localRefData []byte
|
||||
targetDLLData []byte
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
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
|
||||
func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func EnvGetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -66,7 +66,7 @@ func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri
|
||||
}
|
||||
|
||||
// PrintGetEnvInfo - Print the results of the env get command
|
||||
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
|
||||
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
)
|
||||
|
||||
// EnvSetCmd - Set a remote environment variable
|
||||
func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func EnvSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -72,7 +72,7 @@ func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri
|
||||
}
|
||||
|
||||
// PrintSetEnvInfo - Print the set environment info
|
||||
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
|
||||
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
// EnvUnsetCmd - Unset a remote environment variable
|
||||
func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -67,7 +67,7 @@ func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
|
||||
}
|
||||
|
||||
// PrintUnsetEnvInfo - Print the set environment info
|
||||
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) {
|
||||
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
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,16 +25,15 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
|
||||
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory.
|
||||
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -96,7 +95,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: assemblyArgsStr,
|
||||
Arguments: assemblyArgs,
|
||||
Assembly: assemblyBytes,
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
@@ -132,7 +131,7 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar
|
||||
}
|
||||
}
|
||||
|
||||
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
|
||||
@@ -26,19 +26,17 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/term"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ExecuteShellcodeCmd - Execute shellcode in-memory
|
||||
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExecuteShellcodeCmd - Execute shellcode in-memory.
|
||||
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -128,8 +126,8 @@ func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExecuteShellcode - Display result of shellcode execution
|
||||
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||
// PrintExecuteShellcode - Display result of shellcode execution.
|
||||
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverClient) {
|
||||
if task.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", task.Response.GetErr())
|
||||
} else {
|
||||
@@ -137,7 +135,7 @@ func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient
|
||||
}
|
||||
}
|
||||
|
||||
func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||
func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverClient) {
|
||||
// Check active session
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
|
||||
@@ -23,18 +23,16 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ExecuteCmd - Run a command on the remote system
|
||||
func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExecuteCmd - Run a command on the remote system.
|
||||
func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -66,7 +64,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token || hidden || ppid != 0 {
|
||||
if session.OS != "windows" {
|
||||
if (session != nil && session.OS != "windows") || (beacon != nil && beacon.OS != "windows") {
|
||||
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
|
||||
return
|
||||
}
|
||||
@@ -113,7 +111,7 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
|
||||
}
|
||||
}
|
||||
|
||||
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
||||
var lootedOutput []byte
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
@@ -136,8 +134,8 @@ func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName stri
|
||||
PrintExecute(exec, cmd, con)
|
||||
}
|
||||
|
||||
// PrintExecute - Print the output of an executed command
|
||||
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
// PrintExecute - Print the output of an executed command.
|
||||
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverClient) {
|
||||
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
stderr, _ := cmd.Flags().GetString("stderr")
|
||||
@@ -210,7 +208,7 @@ func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStd
|
||||
return []byte(outputString)
|
||||
}
|
||||
|
||||
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverConsoleClient) {
|
||||
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverClient) {
|
||||
if len(commandOutput) == 0 {
|
||||
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
|
||||
return
|
||||
@@ -229,7 +227,7 @@ func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cm
|
||||
loot.SendLootMessage(lootMessage, con)
|
||||
}
|
||||
|
||||
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverConsoleClient) {
|
||||
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverClient) {
|
||||
con.PrintInfof("Output:\n%s", executionOutput)
|
||||
|
||||
if saveOutput {
|
||||
@@ -237,7 +235,7 @@ func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName s
|
||||
}
|
||||
}
|
||||
|
||||
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverConsoleClient) {
|
||||
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverClient) {
|
||||
var outFilePath *os.File
|
||||
var err error
|
||||
|
||||
@@ -258,7 +256,7 @@ func SaveExecutionOutput(executionOutput string, commandName string, hostName st
|
||||
}
|
||||
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(executionOutput))
|
||||
outFilePath.WriteString(executionOutput)
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
@@ -21,19 +21,19 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// MigrateCmd - Windows only, inject an implant into another process
|
||||
func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,44 +43,47 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
|
||||
con.PrintErrorf("Error: Must specify either a PID or process name\n")
|
||||
return
|
||||
}
|
||||
if procName != "" {
|
||||
procCtrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Searching for %s ...", procName), procCtrl)
|
||||
proc, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
procCtrl <- true
|
||||
<-procCtrl
|
||||
for _, p := range proc.GetProcesses() {
|
||||
if strings.EqualFold(p.Executable, procName) {
|
||||
pid = uint32(p.Pid)
|
||||
break
|
||||
}
|
||||
}
|
||||
if pid == 0 {
|
||||
con.PrintErrorf("Error: Could not find process %s\n", procName)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Process name specified, overriding PID with %d\n", pid)
|
||||
|
||||
var config *clientpb.ImplantConfig
|
||||
var implantName string
|
||||
|
||||
if session != nil {
|
||||
config = con.GetActiveSessionConfig()
|
||||
implantName = session.Name
|
||||
} else {
|
||||
config = con.GetActiveBeaconConfig()
|
||||
implantName = beacon.Name
|
||||
}
|
||||
config := con.GetActiveSessionConfig()
|
||||
|
||||
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
|
||||
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
|
||||
encoder = clientpb.ShellcodeEncoder_NONE
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
||||
if pid != 0 {
|
||||
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
||||
} else {
|
||||
con.SpinUntil(fmt.Sprintf("Migrating into %s...", procName), ctrl)
|
||||
}
|
||||
|
||||
/* If the HTTP C2 Config name is not defined, then put in the default value
|
||||
This will have no effect on implants that do not use HTTP C2
|
||||
Also this should be overridden when the build info is pulled from the
|
||||
database, but if there is no build info and we have to create the build
|
||||
from scratch, we need to have something in here.
|
||||
*/
|
||||
if config.HTTPC2ConfigName == "" {
|
||||
config.HTTPC2ConfigName = consts.DefaultC2Profile
|
||||
}
|
||||
|
||||
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
||||
Pid: pid,
|
||||
Config: config,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Encoder: encoder,
|
||||
Pid: pid,
|
||||
Config: config,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Encoder: encoder,
|
||||
Name: implantName,
|
||||
ProcName: procName,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -88,9 +91,32 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
|
||||
con.PrintErrorf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
if !migrate.Success {
|
||||
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
|
||||
return
|
||||
if migrate.Response != nil && migrate.Response.Async {
|
||||
con.AddBeaconCallback(migrate.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, migrate)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
}
|
||||
if !migrate.Success {
|
||||
if migrate.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
|
||||
} else {
|
||||
con.PrintErrorf("Could not migrate into a new process. Check the PID or name.")
|
||||
}
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully migrated to %d\n", migrate.Pid)
|
||||
})
|
||||
con.PrintAsyncResponse(migrate.Response)
|
||||
} else {
|
||||
if !migrate.Success {
|
||||
if migrate.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", migrate.GetResponse().GetErr())
|
||||
} else {
|
||||
con.PrintErrorf("Could not migrate into a new process. Check the PID or name.")
|
||||
}
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully migrated to %d\n", migrate.Pid)
|
||||
}
|
||||
con.PrintInfof("Successfully migrated to %d\n", pid)
|
||||
}
|
||||
@@ -22,18 +22,16 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// MsfInjectCmd - Inject a metasploit payload into a remote process
|
||||
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// MsfInjectCmd - Inject a metasploit payload into a remote process.
|
||||
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -99,8 +97,8 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMsfRemote - Print the results of the remote injection attempt
|
||||
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||
// PrintMsfRemote - Print the results of the remote injection attempt.
|
||||
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverClient) {
|
||||
if msfRemote.Response == nil {
|
||||
con.PrintErrorf("Empty response from msf payload injection task")
|
||||
return
|
||||
|
||||
@@ -22,17 +22,15 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// MsfCmd - Inject a metasploit payload into the current remote process
|
||||
func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// MsfCmd - Inject a metasploit payload into the current remote process.
|
||||
func MsfCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
|
||||
@@ -21,14 +21,11 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
insecureRand "math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
insecureRand "math/rand"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -36,10 +33,11 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// PsExecCmd - psexec command implementation.
|
||||
func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func PsExecCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
|
||||
@@ -25,24 +25,30 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SideloadCmd - Sideload a shared library on the remote system
|
||||
func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// SideloadCmd - Sideload a shared library on the remote system.
|
||||
func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
binPath := args[0]
|
||||
binArgs := strings.Join(args[1:], " ")
|
||||
var binArgs []string
|
||||
if len(args) > 1 {
|
||||
binArgs = args[1:]
|
||||
}
|
||||
|
||||
entryPoint, _ := cmd.Flags().GetString("entry-point")
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
@@ -59,7 +65,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
isDLL := (filepath.Ext(binPath) == ".dll")
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s %v...", binPath, binArgs), ctrl)
|
||||
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: binArgs,
|
||||
@@ -96,7 +102,7 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
|
||||
@@ -22,25 +22,25 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SpawnDllCmd - Spawn execution of a DLL on the remote system
|
||||
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// SpawnDllCmd - Spawn execution of a DLL on the remote system.
|
||||
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
binPath := args[0]
|
||||
dllArgs := strings.Join(args[1:], " ")
|
||||
var dllArgs []string
|
||||
if len(args) > 1 {
|
||||
dllArgs = args[1:]
|
||||
}
|
||||
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
exportName, _ := cmd.Flags().GetString("export")
|
||||
@@ -85,7 +85,7 @@ func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
|
||||
@@ -24,16 +24,15 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
|
||||
func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// 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) {
|
||||
var (
|
||||
privKey []byte
|
||||
err error
|
||||
@@ -120,8 +119,8 @@ func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSSHCmd - Print the ssh command response
|
||||
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
|
||||
// PrintSSHCmd - Print the ssh command response.
|
||||
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverClient) {
|
||||
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
|
||||
if sshCmd.StdErr != "" {
|
||||
@@ -139,7 +138,7 @@ func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient)
|
||||
}
|
||||
}
|
||||
|
||||
func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte) {
|
||||
func tryCredsFromLoot(con *console.SliverClient) (string, string, []byte) {
|
||||
var (
|
||||
username string
|
||||
password string
|
||||
|
||||
@@ -25,12 +25,13 @@ import (
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ExitCmd - Exit the console
|
||||
func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExitCmd - Exit the console.
|
||||
func ExitCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
fmt.Println("Exiting...")
|
||||
if con.IsServer {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
@@ -53,3 +54,15 @@ func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Commands returns the `exit` command.
|
||||
func Command(con *console.SliverClient) []*cobra.Command {
|
||||
return []*cobra.Command{{
|
||||
Use: "exit",
|
||||
Short: "Exit the program",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ExitCmd(cmd, con, args)
|
||||
},
|
||||
GroupID: constants.GenericHelpGroup,
|
||||
}}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Commands returns the “ command and its subcommands.
|
||||
func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
extensionCmd := &cobra.Command{
|
||||
Use: consts.ExtensionsStr,
|
||||
Short: "Manage extensions",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr}),
|
||||
GroupID: consts.ExecutionHelpGroup,
|
||||
Run: func(cmd *cobra.Command, _ []string) {
|
||||
ExtensionsCmd(cmd, con)
|
||||
},
|
||||
}
|
||||
|
||||
extensionCmd.AddCommand(&cobra.Command{
|
||||
Use: consts.ListStr,
|
||||
Short: "List extensions loaded in the current session or beacon",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.ListStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ExtensionsListCmd(cmd, con, args)
|
||||
},
|
||||
})
|
||||
|
||||
extensionLoadCmd := &cobra.Command{
|
||||
Use: consts.LoadStr,
|
||||
Short: "Temporarily load an extension from a local directory",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.LoadStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ExtensionLoadCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
extensionCmd.AddCommand(extensionLoadCmd)
|
||||
carapace.Gen(extensionLoadCmd).PositionalCompletion(carapace.ActionDirectories().Usage("path to the extension directory"))
|
||||
|
||||
extensionInstallCmd := &cobra.Command{
|
||||
Use: consts.InstallStr,
|
||||
Short: "Install an extension from a local directory or .tar.gz file",
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.InstallStr}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ExtensionsInstallCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
extensionCmd.AddCommand(extensionInstallCmd)
|
||||
carapace.Gen(extensionInstallCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to the extension .tar.gz or directory"))
|
||||
|
||||
extensionRmCmd := &cobra.Command{
|
||||
Use: consts.RmStr,
|
||||
Short: "Remove an installed extension",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.RmStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ExtensionsRemoveCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
extensionCmd.AddCommand(extensionRmCmd)
|
||||
carapace.Gen(extensionRmCmd).PositionalCompletion(ExtensionsCommandNameCompleter(con).Usage("the command name of the extension to remove"))
|
||||
|
||||
return []*cobra.Command{extensionCmd}
|
||||
}
|
||||
@@ -24,18 +24,17 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
)
|
||||
|
||||
// ExtensionsCmd - List information about installed extensions
|
||||
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
// ExtensionsCmd - List information about installed extensions.
|
||||
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverClient) {
|
||||
if 0 < len(getInstalledManifests()) {
|
||||
PrintExtensions(con)
|
||||
} else {
|
||||
@@ -43,8 +42,8 @@ func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExtensions - Print a list of loaded extensions
|
||||
func PrintExtensions(con *console.SliverConsoleClient) {
|
||||
// PrintExtensions - Print a list of loaded extensions.
|
||||
func PrintExtensions(con *console.SliverClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
@@ -116,8 +115,8 @@ func getInstalledManifests() map[string]*ExtensionManifest {
|
||||
return installedManifests
|
||||
}
|
||||
|
||||
// ExtensionsCommandNameCompleter - Completer for installed extensions command names
|
||||
func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
// ExtensionsCommandNameCompleter - Completer for installed extensions command names.
|
||||
func ExtensionsCommandNameCompleter(con *console.SliverClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
//installedManifests := getInstalledManifests()
|
||||
results := []string{}
|
||||
|
||||
@@ -25,15 +25,14 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ExtensionsInstallCmd - Install an extension
|
||||
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExtensionsInstallCmd - Install an extension.
|
||||
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
extLocalPath := args[0]
|
||||
|
||||
_, err := os.Stat(extLocalPath)
|
||||
@@ -41,11 +40,11 @@ func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient,
|
||||
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
|
||||
return
|
||||
}
|
||||
InstallFromDir(extLocalPath, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
|
||||
InstallFromDir(extLocalPath, true, con, strings.HasSuffix(extLocalPath, ".tar.gz"))
|
||||
}
|
||||
|
||||
// Install an extension from a directory
|
||||
func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz bool) {
|
||||
func InstallFromDir(extLocalPath string, promptToOverwrite bool, con *console.SliverClient, isGz bool) {
|
||||
var manifestData []byte
|
||||
var err error
|
||||
|
||||
@@ -68,12 +67,14 @@ func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz
|
||||
//create repo path
|
||||
minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestF.Name))
|
||||
if _, err := os.Stat(minstallPath); !os.IsNotExist(err) {
|
||||
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
if promptToOverwrite {
|
||||
con.PrintInfof("Extension '%s' already exists", manifestF.Name)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
forceRemoveAll(minstallPath)
|
||||
}
|
||||
@@ -95,7 +96,7 @@ func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz
|
||||
for _, manifestFile := range manifest.Files {
|
||||
if manifestFile.Path != "" {
|
||||
if isGz {
|
||||
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
|
||||
err = installArtifact(extLocalPath, installPath, manifestFile.Path)
|
||||
} else {
|
||||
src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path))
|
||||
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
|
||||
@@ -107,7 +108,7 @@ func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz
|
||||
}
|
||||
err = util.CopyFile(src, dst)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
err = fmt.Errorf("error copying file '%s' -> '%s': %s", src, dst, err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -120,7 +121,7 @@ func InstallFromDir(extLocalPath string, con *console.SliverConsoleClient, isGz
|
||||
}
|
||||
}
|
||||
|
||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string) error {
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -21,14 +21,13 @@ package extensions
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ExtensionsListCmd - List all extension loaded on the active session/beacon
|
||||
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExtensionsListCmd - List all extension loaded on the active session/beacon.
|
||||
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
|
||||
@@ -21,6 +21,8 @@ package extensions
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -32,12 +34,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
appConsole "github.com/reeflective/console"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -46,12 +42,17 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
appConsole "github.com/reeflective/console"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
|
||||
// ManifestFileName - Extension manifest file name
|
||||
// ManifestFileName - Extension manifest file name.
|
||||
ManifestFileName = "extension.json"
|
||||
)
|
||||
|
||||
@@ -85,7 +86,9 @@ type ExtensionManifest struct {
|
||||
|
||||
ExtCommand []*ExtCommand `json:"commands"`
|
||||
|
||||
RootPath string `json:"-"`
|
||||
RootPath string `json:"-"`
|
||||
ArmoryName string `json:"-"`
|
||||
ArmoryPK string `json:"-"`
|
||||
}
|
||||
|
||||
type ExtCommand struct {
|
||||
@@ -96,6 +99,7 @@ type ExtCommand struct {
|
||||
Arguments []*extensionArgument `json:"arguments"`
|
||||
Entrypoint string `json:"entrypoint"`
|
||||
DependsOn string `json:"depends_on"`
|
||||
Init string `json:"init"`
|
||||
|
||||
Manifest *ExtensionManifest
|
||||
}
|
||||
@@ -134,17 +138,17 @@ func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (strin
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// ExtensionLoadCmd - Load extension command
|
||||
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExtensionLoadCmd - Load extension command.
|
||||
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
manyfest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
manifest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// do not add if the command already exists
|
||||
sliverMenu := con.App.Menu("implant")
|
||||
for _, extCmd := range manyfest.ExtCommand {
|
||||
for _, extCmd := range manifest.ExtCommand {
|
||||
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||
confirm := false
|
||||
@@ -159,7 +163,7 @@ func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
// LoadExtensionManifest - Parse extension files
|
||||
// LoadExtensionManifest - Parse extension files.
|
||||
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
@@ -181,7 +185,7 @@ func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
|
||||
func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
|
||||
ret := &ExtensionManifest{
|
||||
Name: old.CommandName, //treating old commandname as the manifest name to avoid weird chars mostly
|
||||
Name: old.CommandName, //treating old command name as the manifest name to avoid weird chars mostly
|
||||
Version: old.Version,
|
||||
ExtensionAuthor: old.ExtensionAuthor,
|
||||
OriginalAuthor: old.OriginalAuthor,
|
||||
@@ -212,7 +216,9 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
||||
err := json.Unmarshal(data, &extManifest)
|
||||
if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible
|
||||
//maybe it's an old manifest
|
||||
log.Println(err)
|
||||
if err != nil {
|
||||
log.Printf("extension load error: %s", err)
|
||||
}
|
||||
oldmanifest := &ExtensionManifest_{}
|
||||
err := json.Unmarshal(data, &oldmanifest)
|
||||
if err != nil {
|
||||
@@ -262,7 +268,7 @@ func validManifest(manifest *ExtensionManifest) error {
|
||||
}
|
||||
|
||||
// ExtensionRegisterCommand - Register a new extension command
|
||||
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverClient) {
|
||||
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
|
||||
con.PrintErrorf(errInvalidArgs.Error())
|
||||
return
|
||||
@@ -345,7 +351,7 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso
|
||||
cmd.AddCommand(extensionCmd)
|
||||
}
|
||||
|
||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand, cmd *cobra.Command, con *console.SliverClient) error {
|
||||
var extensionList []string
|
||||
binPath, err := ext.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
@@ -366,27 +372,50 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
|
||||
}
|
||||
extensionList = extList.Names
|
||||
}
|
||||
depLoaded := false
|
||||
for _, extName := range extensionList {
|
||||
if !depLoaded {
|
||||
if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn {
|
||||
depLoaded = true
|
||||
//extensionList contains all *implant* loaded extensions. Is a sha256 sum of the relevant file.
|
||||
|
||||
//get the file hash to compare against implant extensions later
|
||||
toberunfilepath, err := ext.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//we need to check the dependent if it's a bof
|
||||
if ext.DependsOn != "" {
|
||||
//verify extension is in loaded list
|
||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
||||
toberunfilepath, err = extension.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// handle error
|
||||
return fmt.Errorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
||||
}
|
||||
if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName {
|
||||
}
|
||||
|
||||
//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 {
|
||||
//check if extension we are trying to run (ext.CommandName or ext.DependsOn, for bofs) is already loaded
|
||||
if extName == toberunhash {
|
||||
//exists on the other side, exit early
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Extension not found, let's load it
|
||||
if filepath.Ext(binPath) == ".o" {
|
||||
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
|
||||
// Auto load the coff loader if we have it
|
||||
if !depLoaded {
|
||||
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
|
||||
return errLoad
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// BOFs are not loaded by the DLL loader, but we need to load the loader (more load more good)
|
||||
if ext.DependsOn != "" {
|
||||
return loadDep(goos, goarch, ext.DependsOn, cmd, con)
|
||||
}
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
@@ -398,14 +427,45 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) error {
|
||||
//set extension name to a hash of the data to avoid loading more than one instance
|
||||
bd := sha256.Sum256(binData)
|
||||
name := hex.EncodeToString(bd[:])
|
||||
sess, beac := con.ActiveTarget.GetInteractive()
|
||||
ctrl := make(chan bool)
|
||||
//first time run of an extension will require some waiting depending on the size
|
||||
if sess != nil {
|
||||
msg := fmt.Sprintf("Sending %s to implant ...", ext.CommandName)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
}
|
||||
//don't block if we are in beacon mode
|
||||
if beac != nil && sess == nil {
|
||||
go func() {
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: name,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error registering extension: %s\n", err)
|
||||
}
|
||||
if registerResp.Response != nil && registerResp.Response.Err != "" {
|
||||
con.PrintErrorf("Error registering extension: %s\n", errors.New(registerResp.Response.Err))
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
//session mode (hopefully)
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: ext.CommandName,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
//Init: ext.Init, ?
|
||||
Name: name,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -415,7 +475,7 @@ func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverClient) error {
|
||||
depExt, ok := loadedExtensions[depName]
|
||||
if ok {
|
||||
depBinPath, err := depExt.getFileForTarget(goos, goarch)
|
||||
@@ -431,7 +491,7 @@ func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con
|
||||
return fmt.Errorf("missing dependency %s", depName)
|
||||
}
|
||||
|
||||
func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
var (
|
||||
err error
|
||||
extensionArgs []byte
|
||||
@@ -488,16 +548,10 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
|
||||
} else {
|
||||
// Regular DLL
|
||||
// extArgs := strings.Join(args, " ")
|
||||
//legacy case - single string arg
|
||||
if len(ext.Arguments) == 1 && ext.Arguments[0].Type == "string" {
|
||||
extensionArgs = []byte(strings.Join(args, " "))
|
||||
} else {
|
||||
extensionArgs, err = getExtArgs(cmd, args, binPath, ext)
|
||||
if err != nil {
|
||||
con.PrintErrorf("ext args error: %s\n", err)
|
||||
return
|
||||
}
|
||||
extensionArgs, err = getExtArgs(cmd, args, binPath, ext)
|
||||
if err != nil {
|
||||
con.PrintErrorf("ext args error: %s\n", err)
|
||||
return
|
||||
}
|
||||
extName = ext.CommandName
|
||||
entryPoint = ext.Entrypoint
|
||||
@@ -506,8 +560,41 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s ...", cmd.Name())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
extdata, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("ext read file error: %s\n", err)
|
||||
}
|
||||
bd := sha256.Sum256(extdata)
|
||||
name := hex.EncodeToString(bd[:])
|
||||
if isBOF {
|
||||
//if we are using a bof, we are actually calling the coffloader extension - so get the file from dep ref and use that shasum
|
||||
|
||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
||||
dep, err := extension.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not get file for extension %s", ext.DependsOn)
|
||||
return
|
||||
}
|
||||
depdata, err := os.ReadFile(dep)
|
||||
if err != nil {
|
||||
con.PrintErrorf("dep read file error: %s\n", err)
|
||||
return
|
||||
}
|
||||
if len(depdata) == 0 {
|
||||
con.PrintErrorf("read empty file: %s\n", dep)
|
||||
return
|
||||
}
|
||||
bd = sha256.Sum256(depdata)
|
||||
name = hex.EncodeToString(bd[:])
|
||||
} else {
|
||||
// handle error
|
||||
con.PrintErrorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: extName,
|
||||
Name: name,
|
||||
Export: entryPoint,
|
||||
Args: extensionArgs,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
@@ -534,8 +621,8 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExtOutput - Print the ext execution output
|
||||
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
|
||||
// PrintExtOutput - Print the ext execution output.
|
||||
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverClient) {
|
||||
if extName == commandName {
|
||||
con.PrintInfof("Successfully executed %s", extName)
|
||||
} else {
|
||||
@@ -550,7 +637,7 @@ func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.
|
||||
}
|
||||
}
|
||||
|
||||
func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtCommand) ([]byte, error) {
|
||||
func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) {
|
||||
var err error
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
@@ -559,6 +646,12 @@ func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
|
||||
// Parse BOF arguments from grumble
|
||||
missingRequiredArgs := make([]string, 0)
|
||||
|
||||
// If we have an extension that expects a single string, but more than one has been parsed, combine them
|
||||
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") && len(args) > 0 {
|
||||
// The loop below will only read the first element of args because ext.Arguments is 1
|
||||
args[0] = strings.Join(args, " ")
|
||||
}
|
||||
|
||||
for _, arg := range ext.Arguments {
|
||||
// If we don't have any positional words left to consume,
|
||||
// add the remaining required extension arguments in the
|
||||
@@ -666,7 +759,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
|
||||
return extensionArgs, nil
|
||||
}
|
||||
|
||||
// CmdExists - checks if a command exists
|
||||
// CmdExists - checks if a command exists.
|
||||
func CmdExists(name string, cmd *cobra.Command) bool {
|
||||
for _, c := range cmd.Commands() {
|
||||
if name == c.Name() {
|
||||
|
||||
@@ -25,15 +25,14 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ExtensionsRemoveCmd - Remove an extension
|
||||
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ExtensionsRemoveCmd - Remove an extension.
|
||||
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
name := args[0]
|
||||
if name == "" {
|
||||
con.PrintErrorf("Extension name is required\n")
|
||||
@@ -64,8 +63,8 @@ func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveExtensionByCommandName - Remove an extension by command name
|
||||
func RemoveExtensionByCommandName(commandName string, con *console.SliverConsoleClient) error {
|
||||
// RemoveExtensionByCommandName - Remove an extension by command name.
|
||||
func RemoveExtensionByCommandName(commandName string, con *console.SliverClient) error {
|
||||
if commandName == "" {
|
||||
return errors.New("command name is required")
|
||||
}
|
||||
@@ -82,7 +81,7 @@ func RemoveExtensionByCommandName(commandName string, con *console.SliverConsole
|
||||
}
|
||||
|
||||
// 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.SliverConsoleClient) (bool, error) {
|
||||
func RemoveExtensionByManifestName(manifestName string, con *console.SliverClient) (bool, error) {
|
||||
if manifestName == "" {
|
||||
return false, errors.New("command name is required")
|
||||
}
|
||||
|
||||
@@ -27,18 +27,17 @@ import (
|
||||
"github.com/alecthomas/chroma/formatters"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// CatCmd - Display the contents of a remote file
|
||||
func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CatCmd - Display the contents of a remote file.
|
||||
func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -81,8 +80,8 @@ func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCat - Print the download to stdout
|
||||
func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
// PrintCat - Print the download to stdout.
|
||||
func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverClient) {
|
||||
var (
|
||||
lootDownload bool = true
|
||||
err error
|
||||
|
||||
@@ -21,17 +21,15 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// CdCmd - Change directory on the remote system
|
||||
func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// CdCmd - Change directory on the remote system.
|
||||
func CdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
|
||||
@@ -20,17 +20,15 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ChmodCmd - Change the permissions of a file on the remote file system
|
||||
func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ChmodCmd - Change the permissions of a file on the remote file system.
|
||||
func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -77,8 +75,8 @@ func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin
|
||||
}
|
||||
}
|
||||
|
||||
// PrintChmod - Print the chmod response
|
||||
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverConsoleClient) {
|
||||
// PrintChmod - Print the chmod response.
|
||||
func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverClient) {
|
||||
if chmod.Response != nil && chmod.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", chmod.Response.Err)
|
||||
return
|
||||
|
||||
@@ -20,17 +20,15 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ChownCmd - Change the owner of a file on the remote file system
|
||||
func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// ChownCmd - Change the owner of a file on the remote file system.
|
||||
func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -85,8 +83,8 @@ func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin
|
||||
}
|
||||
}
|
||||
|
||||
// PrintChown - Print the chown response
|
||||
func PrintChown(chown *sliverpb.Chown, con *console.SliverConsoleClient) {
|
||||
// PrintChown - Print the chown response.
|
||||
func PrintChown(chown *sliverpb.Chown, con *console.SliverClient) {
|
||||
if chown.Response != nil && chown.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", chown.Response.Err)
|
||||
return
|
||||
|
||||
@@ -21,17 +21,15 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ChtimesCmd - Change the access and modified time of a file on the remote file system
|
||||
func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// 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) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -98,8 +96,8 @@ func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str
|
||||
}
|
||||
}
|
||||
|
||||
// PrintChtimes - Print the Chtimes response
|
||||
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverConsoleClient) {
|
||||
// PrintChtimes - Print the Chtimes response.
|
||||
func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverClient) {
|
||||
if chtimes.Response != nil && chtimes.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", chtimes.Response.Err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
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", 0, "Grab the first number of lines from the file")
|
||||
})
|
||||
carapace.Gen(headCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print"))
|
||||
|
||||
tailCmd := &cobra.Command{
|
||||
Use: consts.TailStr,
|
||||
Short: "Grab the last number of bytes or lines from a file",
|
||||
Long: help.GetHelpFor([]string{consts.TailStr}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
/*
|
||||
The last argument tells head if the user requested the head or tail of the file
|
||||
True means head, false means tail
|
||||
*/
|
||||
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", 0, "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,16 +21,14 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
|
||||
func CpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -72,7 +70,7 @@ func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
|
||||
return
|
||||
}
|
||||
|
||||
func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) {
|
||||
func PrintCp(cp *sliverpb.Cp, con *console.SliverClient) {
|
||||
if cp.Response != nil && cp.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", cp.Response.Err)
|
||||
return
|
||||
|
||||
@@ -28,18 +28,17 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
)
|
||||
|
||||
func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func DownloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -105,7 +104,7 @@ func prettifyDownloadName(path string) string {
|
||||
return filteredString
|
||||
}
|
||||
|
||||
func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverConsoleClient) {
|
||||
func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverClient) {
|
||||
var err error
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
|
||||
@@ -83,7 +83,7 @@ func processFlags(searchPattern string, insensitive bool, exact bool) string {
|
||||
return processedSearchPattern
|
||||
}
|
||||
|
||||
func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func GrepCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -152,7 +152,7 @@ func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string
|
||||
}
|
||||
|
||||
// printGrep - Print the results from the grep operation to stdout
|
||||
func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
colorize, _ := cmd.Flags().GetBool("colorize-output")
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string, head bool) {
|
||||
func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head bool) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
|
||||
@@ -27,19 +27,17 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// LsCmd - List the contents of a remote directory
|
||||
func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// LsCmd - List the contents of a remote directory.
|
||||
func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -75,8 +73,8 @@ func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintLs - Display an sliverpb.Ls object
|
||||
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverConsoleClient) {
|
||||
// PrintLs - Display an sliverpb.Ls object.
|
||||
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
|
||||
@@ -20,16 +20,15 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// MemfilesAddCmd - Add memfile
|
||||
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// MemfilesAddCmd - Add memfile.
|
||||
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -57,8 +56,8 @@ func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [
|
||||
}
|
||||
}
|
||||
|
||||
// PrintAddMemfile - Print the memfiles response
|
||||
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverConsoleClient) {
|
||||
// PrintAddMemfile - Print the memfiles response.
|
||||
func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverClient) {
|
||||
if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", memfilesAdd.Response.Err)
|
||||
return
|
||||
|
||||
@@ -25,17 +25,16 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// MemfilesListCmd - List memfiles
|
||||
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// MemfilesListCmd - List memfiles.
|
||||
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -63,8 +62,8 @@ func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMemfiles - Display an sliverpb.Ls object
|
||||
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
|
||||
// PrintMemfiles - Display an sliverpb.Ls object.
|
||||
func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
|
||||
@@ -21,16 +21,15 @@ import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// MemfilesRmCmd - Remove a memfile
|
||||
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// MemfilesRmCmd - Remove a memfile.
|
||||
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -70,8 +69,8 @@ func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRmMemfile - Remove a memfile
|
||||
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverConsoleClient) {
|
||||
// PrintRmMemfile - Remove a memfile.
|
||||
func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverClient) {
|
||||
if memfilesList.Response != nil && memfilesList.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", memfilesList.Response.Err)
|
||||
return
|
||||
|
||||
@@ -21,17 +21,15 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// MkdirCmd - Make a remote directory
|
||||
func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// MkdirCmd - Make a remote directory.
|
||||
func MkdirCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -68,8 +66,8 @@ func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMkdir - Print make directory
|
||||
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverConsoleClient) {
|
||||
// PrintMkdir - Print make directory.
|
||||
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverClient) {
|
||||
if mkdir.Response != nil && mkdir.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", mkdir.Response.Err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
// Drive mappings for Windows
|
||||
var driveTypeMap = map[string]string{
|
||||
"0": "Unknown",
|
||||
"1": "Root Path invalid (no volume mounted for path)",
|
||||
"2": "Removable",
|
||||
"3": "Fixed disk",
|
||||
"4": "Remote / network drive",
|
||||
"5": "CD-ROM",
|
||||
"6": "RAM disk",
|
||||
}
|
||||
|
||||
// MountCmd - Print information about mounted filesystems
|
||||
func MountCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
mount, err := con.Rpc.Mount(context.Background(), &sliverpb.MountReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
os := getOS(session, beacon)
|
||||
if mount.Response != nil && mount.Response.Async {
|
||||
con.AddBeaconCallback(mount.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, mount)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintMount(os, mount, con)
|
||||
})
|
||||
con.PrintAsyncResponse(mount.Response)
|
||||
} else {
|
||||
PrintMount(os, mount, con)
|
||||
}
|
||||
}
|
||||
|
||||
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.OS
|
||||
} else if beacon != nil {
|
||||
return beacon.OS
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func reduceSpaceMetric(numberOfBytes float64) string {
|
||||
units := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||
|
||||
if numberOfBytes < 1 {
|
||||
return fmt.Sprintf("0 %s", units[0])
|
||||
}
|
||||
|
||||
base := 1024.0
|
||||
|
||||
exp := math.Floor(math.Log(numberOfBytes) / math.Log(base))
|
||||
index := int(math.Min(exp, float64(len(units)-1)))
|
||||
divisor := math.Pow(base, float64(index))
|
||||
|
||||
value := numberOfBytes / divisor
|
||||
|
||||
return fmt.Sprintf("%.2f %s", value, units[index])
|
||||
}
|
||||
|
||||
// PrintMount - Print a table containing information on mounted filesystems
|
||||
func PrintMount(os string, mount *sliverpb.Mount, con *console.SliverClient) {
|
||||
if mount.Response != nil && mount.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", mount.Response.Err)
|
||||
return
|
||||
}
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
|
||||
switch os {
|
||||
case "windows":
|
||||
tw.AppendHeader(table.Row{"Volume", "Volume Type", "Mount Point", "Label", "Filesystem", "Used Space", "Free Space", "Total Space"})
|
||||
case "darwin":
|
||||
fallthrough
|
||||
case "linux":
|
||||
fallthrough
|
||||
default:
|
||||
tw.AppendHeader(table.Row{"Source", "Mount Point", "Mount Root", "Filesystem Type", "Options", "Total Space"})
|
||||
}
|
||||
|
||||
for _, mountPoint := range mount.Info {
|
||||
row := mountRow(os, mountPoint)
|
||||
tw.AppendRow(row)
|
||||
}
|
||||
|
||||
settings.PaginateTable(tw, 0, false, false, con)
|
||||
}
|
||||
|
||||
func mountRow(os string, mountInfo *sliverpb.MountInfo) table.Row {
|
||||
var row table.Row
|
||||
|
||||
switch os {
|
||||
case "windows":
|
||||
// Translate VolumeType
|
||||
volType, ok := driveTypeMap[mountInfo.VolumeType]
|
||||
if !ok {
|
||||
volType = driveTypeMap["0"]
|
||||
}
|
||||
row = table.Row{mountInfo.VolumeName,
|
||||
volType,
|
||||
mountInfo.MountPoint,
|
||||
mountInfo.Label,
|
||||
mountInfo.FileSystem,
|
||||
reduceSpaceMetric(float64(mountInfo.UsedSpace)),
|
||||
reduceSpaceMetric(float64(mountInfo.FreeSpace)),
|
||||
reduceSpaceMetric(float64(mountInfo.TotalSpace)),
|
||||
}
|
||||
case "darwin":
|
||||
fallthrough
|
||||
case "linux":
|
||||
fallthrough
|
||||
default:
|
||||
row = table.Row{mountInfo.VolumeName,
|
||||
mountInfo.MountPoint,
|
||||
mountInfo.Label,
|
||||
mountInfo.VolumeType,
|
||||
mountInfo.MountOptions,
|
||||
reduceSpaceMetric(float64(mountInfo.TotalSpace)),
|
||||
}
|
||||
}
|
||||
return row
|
||||
}
|
||||
@@ -21,16 +21,14 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func MvCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
|
||||
func MvCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -77,8 +75,8 @@ func MvCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
|
||||
return
|
||||
}
|
||||
|
||||
// PrintMv - Print the renamed file
|
||||
func PrintMv(mv *sliverpb.Mv, con *console.SliverConsoleClient) {
|
||||
// PrintMv - Print the renamed file.
|
||||
func PrintMv(mv *sliverpb.Mv, con *console.SliverClient) {
|
||||
if mv.Response != nil && mv.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", mv.Response.Err)
|
||||
return
|
||||
|
||||
@@ -21,17 +21,15 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// PwdCmd - Print the remote working directory
|
||||
func PwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// PwdCmd - Print the remote working directory.
|
||||
func PwdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -58,8 +56,8 @@ func PwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintPwd - Print the remote working directory
|
||||
func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverConsoleClient) {
|
||||
// PrintPwd - Print the remote working directory.
|
||||
func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverClient) {
|
||||
if pwd.Response != nil && pwd.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", pwd.Response.Err)
|
||||
return
|
||||
|
||||
@@ -21,17 +21,15 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// RmCmd - Remove a directory from the remote file system
|
||||
func RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// RmCmd - Remove a directory from the remote file system.
|
||||
func RmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -73,8 +71,8 @@ func RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRm - Print the rm response
|
||||
func PrintRm(rm *sliverpb.Rm, con *console.SliverConsoleClient) {
|
||||
// PrintRm - Print the rm response.
|
||||
func PrintRm(rm *sliverpb.Rm, con *console.SliverClient) {
|
||||
if rm.Response != nil && rm.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", rm.Response.Err)
|
||||
return
|
||||
|
||||
@@ -30,15 +30,13 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/implant/sliver/handlers/matcher"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func determineDirPathFilter(targetPath string) (string, string) {
|
||||
@@ -266,7 +264,7 @@ func tarDirectory(sourcePath string, pathAsSpecified string, sourceFilter string
|
||||
}
|
||||
|
||||
// UploadCmd - Upload a file to the remote system
|
||||
func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
func UploadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
readFiles := 0
|
||||
unreadableFiles := 0
|
||||
var isDirectory bool
|
||||
@@ -423,8 +421,8 @@ func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri
|
||||
}
|
||||
}
|
||||
|
||||
// PrintUpload - Print the result of the upload command
|
||||
func PrintUpload(upload *sliverpb.Upload, con *console.SliverConsoleClient) {
|
||||
// PrintUpload - Print the result of the upload command.
|
||||
func PrintUpload(upload *sliverpb.Upload, con *console.SliverClient) {
|
||||
if upload.Response != nil && upload.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", upload.Response.Err)
|
||||
return
|
||||
|
||||
Loaded 100 of 3306 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user