mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-07 17:08:13 -10:00
Compare commits
378
Commits
hosts
...
stage-socks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e66d563f65 | ||
|
|
8dd2b5d7ce | ||
|
|
10246681ba | ||
|
|
ed19017941 | ||
|
|
c0116cc9d5 | ||
|
|
a890812a0a | ||
|
|
b1922bce73 | ||
|
|
47aaeb0225 | ||
|
|
61c844db44 | ||
|
|
35bbc61a9a | ||
|
|
6453f477f4 | ||
|
|
b0709a364a | ||
|
|
2e65e1842f | ||
|
|
c068664854 | ||
|
|
e9debcc2a4 | ||
|
|
7ae2d0a481 | ||
|
|
b62e3354e0 | ||
|
|
e55afa7efe | ||
|
|
65dc11a40e | ||
|
|
2620673f50 | ||
|
|
e22d3ac58e | ||
|
|
604c3480cf | ||
|
|
ca8544a169 | ||
|
|
e1713bd418 | ||
|
|
0649a2680b | ||
|
|
280f5c6be7 | ||
|
|
77ddfd8013 | ||
|
|
48d2eed385 | ||
|
|
36d531fa86 | ||
|
|
ab2ab45edc | ||
|
|
2b2c1bdc13 | ||
|
|
983b5476bd | ||
|
|
cbd95a2003 | ||
|
|
a3b1693a41 | ||
|
|
434ca2fee9 | ||
|
|
92ef847a59 | ||
|
|
800c584397 | ||
|
|
b2a2dac63b | ||
|
|
954616b5a3 | ||
|
|
2cd4264101 | ||
|
|
b8d78c56e9 | ||
|
|
c5262aa807 | ||
|
|
17dba3543d | ||
|
|
7b0a83027e | ||
|
|
f46f7a36ff | ||
|
|
a369c46952 | ||
|
|
907ef9ca2f | ||
|
|
4df5c78b7e | ||
|
|
3765e016b4 | ||
|
|
6fa06e8d74 | ||
|
|
c3841efcef | ||
|
|
508e9ab9d9 | ||
|
|
433482031c | ||
|
|
a4a94eca98 | ||
|
|
ecea6049a8 | ||
|
|
1767c34f47 | ||
|
|
6fd1e4802b | ||
|
|
f745d9524e | ||
|
|
a8464d5b0c | ||
|
|
cc7a56e07b | ||
|
|
cdbf93d64b | ||
|
|
fdd319f6f3 | ||
|
|
22f840acbd | ||
|
|
9d200bb2aa | ||
|
|
17b6cf7a5e | ||
|
|
22efd18301 | ||
|
|
4d45883c72 | ||
|
|
b98bf6ef53 | ||
|
|
65ea935750 | ||
|
|
9865ee2396 | ||
|
|
ca7cf74387 | ||
|
|
9eb6fda037 | ||
|
|
1a5eb1c118 | ||
|
|
f43d01b4b0 | ||
|
|
96fe3c7db8 | ||
|
|
8188ba689d | ||
|
|
4517684728 | ||
|
|
ab1b72f410 | ||
|
|
fd83024e4b | ||
|
|
18ee8e5133 | ||
|
|
5a96962611 | ||
|
|
85b592b2b9 | ||
|
|
91a569f389 | ||
|
|
b646ca835c | ||
|
|
26993f797c | ||
|
|
ec743b2e85 | ||
|
|
0ac43c69d6 | ||
|
|
a2f8bac4b1 | ||
|
|
2c9643b404 | ||
|
|
1239b5fd87 | ||
|
|
d904a74d14 | ||
|
|
60aaeebe0b | ||
|
|
000ecb51eb | ||
|
|
fbddd67930 | ||
|
|
10f7b000b4 | ||
|
|
2f3ebbfcd5 | ||
|
|
b54d97b497 | ||
|
|
eedfb272a5 | ||
|
|
47857e903c | ||
|
|
0da314d925 | ||
|
|
c132b14272 | ||
|
|
f6b7e07653 | ||
|
|
60cbf09520 | ||
|
|
0090e8dfd8 | ||
|
|
099753fa74 | ||
|
|
6685bf486e | ||
|
|
6d5a288626 | ||
|
|
3bf157b29d | ||
|
|
146be0e814 | ||
|
|
065fc3ffa6 | ||
|
|
d3da84731f | ||
|
|
b451d90a33 | ||
|
|
c01ace4a67 | ||
|
|
e8d22af3bc | ||
|
|
50f83c958b | ||
|
|
6ac9a16aa7 | ||
|
|
caad9de26c | ||
|
|
70805d04f7 | ||
|
|
a3b98716a7 | ||
|
|
24f6a9d8ab | ||
|
|
d6b5571c7e | ||
|
|
c0b08cbd42 | ||
|
|
d567a21f8c | ||
|
|
105d80cdb7 | ||
|
|
d1627c9030 | ||
|
|
661732012d | ||
|
|
f55f248fbc | ||
|
|
6817c5a117 | ||
|
|
6b31d6985a | ||
|
|
47357c346a | ||
|
|
970ff6b80b | ||
|
|
126dc44c44 | ||
|
|
4abc395485 | ||
|
|
0f5284fb2d | ||
|
|
0f4dc27498 | ||
|
|
7ea5039e9a | ||
|
|
b1e4a016d4 | ||
|
|
785810ebea | ||
|
|
5779f337d0 | ||
|
|
ab93f0d1c6 | ||
|
|
d4cc59d1e6 | ||
|
|
03d1f92441 | ||
|
|
77ddf234bd | ||
|
|
a53bc17665 | ||
|
|
1682f58801 | ||
|
|
3d4e76990c | ||
|
|
d97fb249a8 | ||
|
|
e8714dbdec | ||
|
|
f69a4f8b92 | ||
|
|
c2925a8a57 | ||
|
|
6484b0c5a6 | ||
|
|
338b8bb5b2 | ||
|
|
15ca3871dd | ||
|
|
f11a1333bb | ||
|
|
4a2c9cb50e | ||
|
|
c8928b4864 | ||
|
|
13ecb9782b | ||
|
|
05ce79e986 | ||
|
|
a3c7d057f6 | ||
|
|
b3277d0f4c | ||
|
|
1f0e9d6995 | ||
|
|
478f677f55 | ||
|
|
7463298de1 | ||
|
|
7306d439e1 | ||
|
|
66d6e8043a | ||
|
|
11505d8f94 | ||
|
|
452d7b7fce | ||
|
|
606c5ac948 | ||
|
|
8ee4f786b4 | ||
|
|
79a96368d0 | ||
|
|
8176068bb5 | ||
|
|
ff8de9922e | ||
|
|
13cbf53851 | ||
|
|
56f339c8a3 | ||
|
|
e31fd56730 | ||
|
|
9af5d9126f | ||
|
|
d9499c26a1 | ||
|
|
5bd101bc47 | ||
|
|
0e1d21bb28 | ||
|
|
1f5a950752 | ||
|
|
3a1ff3fa52 | ||
|
|
9db930b10f | ||
|
|
2fa6bc2e59 | ||
|
|
2e3f92ed22 | ||
|
|
c552a2fd9f | ||
|
|
e729576a6d | ||
|
|
c9f04b0a1c | ||
|
|
a439c39d64 | ||
|
|
0471a219c4 | ||
|
|
6631727242 | ||
|
|
c21171d739 | ||
|
|
98aa4ed58c | ||
|
|
4ad1f1a561 | ||
|
|
d0a38bb792 | ||
|
|
ee8603b30c | ||
|
|
11ea01819f | ||
|
|
da39a82b83 | ||
|
|
3ce730a469 | ||
|
|
f52ee8c28f | ||
|
|
150511df9b | ||
|
|
3e8c3fc02e | ||
|
|
019d0d40f6 | ||
|
|
268aa95169 | ||
|
|
7775880277 | ||
|
|
ba541ffd43 | ||
|
|
a60cc86c9a | ||
|
|
0099e06cc6 | ||
|
|
b0fdec2700 | ||
|
|
8419cf7442 | ||
|
|
d470543f98 | ||
|
|
84d41f063b | ||
|
|
c876f34cd0 | ||
|
|
7ba3f87afb | ||
|
|
008f9c6fde | ||
|
|
80e6c2a24e | ||
|
|
69842674d2 | ||
|
|
b2f9ccfab6 | ||
|
|
358b6ab20d | ||
|
|
268db3ce00 | ||
|
|
03dfb2f5d9 | ||
|
|
d4e809d889 | ||
|
|
6f6fd8f0f5 | ||
|
|
f5e374293c | ||
|
|
8f12e35124 | ||
|
|
b27809d9ba | ||
|
|
377d56bda3 | ||
|
|
4bcb0cb61f | ||
|
|
9d6870e792 | ||
|
|
93b1e3a7b4 | ||
|
|
90f799a6f2 | ||
|
|
251423bc28 | ||
|
|
03957f4d01 | ||
|
|
a7d07e372a | ||
|
|
364b0d6f57 | ||
|
|
52ef179988 | ||
|
|
79a50d24fe | ||
|
|
4e23091147 | ||
|
|
98e7f7abf8 | ||
|
|
92e833a481 | ||
|
|
b442f95a7b | ||
|
|
da8d5b8a0c | ||
|
|
3151b559a6 | ||
|
|
b02489a5d3 | ||
|
|
eb57802c28 | ||
|
|
767644ddc3 | ||
|
|
185913c6eb | ||
|
|
e3093bee6e | ||
|
|
f6014393be | ||
|
|
90de40f1ee | ||
|
|
43548ad4b2 | ||
|
|
09358666a1 | ||
|
|
213ad330c5 | ||
|
|
f7d7bdc677 | ||
|
|
c292ac4b28 | ||
|
|
f576c138c9 | ||
|
|
216054e90c | ||
|
|
62e311fb6f | ||
|
|
90de32b6f2 | ||
|
|
cc2a5bb622 | ||
|
|
b8b20711fe | ||
|
|
3d7889183a | ||
|
|
d2297f9423 | ||
|
|
de7fd664cd | ||
|
|
f87582462d | ||
|
|
1549b894fa | ||
|
|
32119e09dc | ||
|
|
4dd5319987 | ||
|
|
860452c418 | ||
|
|
87fba59c57 | ||
|
|
ab58a67df4 | ||
|
|
424db7bdca | ||
|
|
cec1451dec | ||
|
|
4d64866a15 | ||
|
|
1d859f99c8 | ||
|
|
e79e3daf7d | ||
|
|
1a632f48ff | ||
|
|
83adb36bbd | ||
|
|
686452a65e | ||
|
|
e436e9ca89 | ||
|
|
db29281233 | ||
|
|
65cea9eeb2 | ||
|
|
06a44f980b | ||
|
|
952f22583e | ||
|
|
5429c8d45a | ||
|
|
9e8bfb62d7 | ||
|
|
45ef2a27a9 | ||
|
|
93a1492a05 | ||
|
|
b554b49b42 | ||
|
|
e7d8495cb0 | ||
|
|
2ebb2734bf | ||
|
|
c3f5f2bf85 | ||
|
|
515b927791 | ||
|
|
35489e974f | ||
|
|
e2cf688044 | ||
|
|
79772297bc | ||
|
|
7d4de43559 | ||
|
|
7df290386e | ||
|
|
fc5673b33e | ||
|
|
31a53a4cba | ||
|
|
8967bcf35d | ||
|
|
6d4b28731e | ||
|
|
235201f701 | ||
|
|
d1135d1d68 | ||
|
|
a998a571f9 | ||
|
|
ce3cee731c | ||
|
|
9e924efcb2 | ||
|
|
23309233a8 | ||
|
|
e107382d07 | ||
|
|
9ea9d1add8 | ||
|
|
bc5a622944 | ||
|
|
c24a37edcd | ||
|
|
536171e8ac | ||
|
|
4d17f0ed66 | ||
|
|
1e5bf9a39c | ||
|
|
18cb8d142d | ||
|
|
b3467e0512 | ||
|
|
8a1845baf8 | ||
|
|
00a2faf137 | ||
|
|
6fb3835e14 | ||
|
|
f3144eb919 | ||
|
|
83c5fad8eb | ||
|
|
bfe2013eb5 | ||
|
|
0923de192c | ||
|
|
c41e9dead0 | ||
|
|
e989ba581b | ||
|
|
7f9657f32d | ||
|
|
8cc3f1984a | ||
|
|
3c07043531 | ||
|
|
b60e826a02 | ||
|
|
d337d4f13c | ||
|
|
80b40341d8 | ||
|
|
c4101f502d | ||
|
|
75dd1b1a26 | ||
|
|
359746120a | ||
|
|
b08728765d | ||
|
|
69d99e28fd | ||
|
|
a811b53561 | ||
|
|
cbfae11876 | ||
|
|
b7a814f0c5 | ||
|
|
572e717634 | ||
|
|
4bf7246b0a | ||
|
|
febf50e464 | ||
|
|
59bdaa7469 | ||
|
|
cce67c289a | ||
|
|
97e7df764c | ||
|
|
33e03df1c9 | ||
|
|
d8fc1244d6 | ||
|
|
93e5d21941 | ||
|
|
7f31a9ea3e | ||
|
|
242afb0011 | ||
|
|
a427f86c4e | ||
|
|
93935b1691 | ||
|
|
a13a4dc12e | ||
|
|
4fa4562bea | ||
|
|
61b86a4a9a | ||
|
|
4c2a309054 | ||
|
|
465eb8cfee | ||
|
|
55da1a5dde | ||
|
|
bf44f379ec | ||
|
|
e5430caf8d | ||
|
|
60c5c2ee89 | ||
|
|
9cf607d155 | ||
|
|
e3a6b237f1 | ||
|
|
2c70ff1291 | ||
|
|
4a3a2a7be3 | ||
|
|
2a8f9bca18 | ||
|
|
d46a67c840 | ||
|
|
8b99a98d5e | ||
|
|
043eda5bc4 | ||
|
|
a51d93cf58 | ||
|
|
44abb0e03b | ||
|
|
09d94ab559 | ||
|
|
1420ebd447 | ||
|
|
96a531f9d4 | ||
|
|
7ef4923fc0 | ||
|
|
3f83761a90 | ||
|
|
f3bdb5cadc | ||
|
|
85265d663f |
No files matched your search
+15
-22
@@ -1,28 +1,21 @@
|
||||
assets/*.zip
|
||||
assets/darwin
|
||||
assets/linux
|
||||
assets/windows
|
||||
server/assets/fs/**.zip
|
||||
/server/assets/fs/**.zip
|
||||
|
||||
*.sdk.tar.xz
|
||||
/*.sdk.tar.xz
|
||||
|
||||
sliver-server
|
||||
sliver-server.exe
|
||||
/sliver-server
|
||||
/sliver-server.exe
|
||||
|
||||
sliver-client
|
||||
sliver-client.exe
|
||||
/sliver-client
|
||||
/sliver-client.exe
|
||||
|
||||
gui/
|
||||
gui/*
|
||||
/.vscode/
|
||||
/.vscode/*
|
||||
|
||||
.vscode/
|
||||
.vscode/*
|
||||
/.github/
|
||||
/.github/*
|
||||
|
||||
.github/
|
||||
.github/*
|
||||
|
||||
build.py
|
||||
LICENSE
|
||||
SECURITY.md
|
||||
README.md
|
||||
CONTRIBUTING.md
|
||||
/build.py
|
||||
/LICENSE
|
||||
/SECURITY.md
|
||||
/README.md
|
||||
/CONTRIBUTING.md
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -59,10 +59,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
@@ -17,10 +17,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.16"
|
||||
go-version: "^1.17"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -15,10 +15,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -50,10 +50,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
+8
-7
@@ -1,10 +1,11 @@
|
||||
.vscode/*
|
||||
.devcontainer
|
||||
/.vscode/*
|
||||
/.devcontainer
|
||||
/.idea
|
||||
|
||||
Dockerfile.*
|
||||
*.sdk.tar.xz
|
||||
/Dockerfile.*
|
||||
/*.sdk.tar.xz
|
||||
|
||||
release-*
|
||||
/release-*
|
||||
|
||||
# Diffs
|
||||
*.orig
|
||||
@@ -30,10 +31,10 @@ sliver-server-windows.zip
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
/*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
/*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
+2
-1
@@ -24,9 +24,10 @@ Contributing to Sliver
|
||||
* _Never_ use homegrown or non-peer reviewed encryption or random number generation algorithms.
|
||||
* Whenever possible, use the following algorithms/encryption modes:
|
||||
- AES-GCM-256
|
||||
- RSA-OAEP-2048 / RSA-OAEP-4096
|
||||
- SHA2-256 / HMAC-SHA2-256 or higher (e.g. SHA2-384)
|
||||
- Curves P521, P384, P256
|
||||
- Curve25519, XSalsa20, and Poly1305 (Nacl)
|
||||
- ChaCha20Poly1305
|
||||
* _Never_ use the following in a security context, and _avoid_ use even in a non-security context:
|
||||
- MD5
|
||||
- SHA1
|
||||
|
||||
+11
-8
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.16.6
|
||||
FROM golang:1.17.2
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
@@ -42,26 +42,29 @@ RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
|
||||
# > Sliver
|
||||
#
|
||||
|
||||
# protoc
|
||||
# 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}
|
||||
|
||||
# assets
|
||||
# Go assets
|
||||
WORKDIR /go/src/github.com/bishopfox/sliver
|
||||
ADD ./go-assets.sh /go/src/github.com/bishopfox/sliver/go-assets.sh
|
||||
RUN ./go-assets.sh
|
||||
|
||||
# Compile sliver server
|
||||
ADD . /go/src/github.com/bishopfox/sliver/
|
||||
RUN go mod vendor && make linux && cp -vv sliver-server /opt/sliver-server
|
||||
RUN make \
|
||||
&& cp -vv sliver-server /opt/sliver-server \
|
||||
&& /opt/sliver-server unpack --force
|
||||
|
||||
RUN ls -lah \
|
||||
&& /opt/sliver-server unpack --force \
|
||||
&& /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
# Run unit tests
|
||||
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
|
||||
# Clean up
|
||||
RUN make clean \
|
||||
&& rm -rf /go/src/* \
|
||||
&& rm -rf /home/sliver/.sliver
|
||||
|
||||
@@ -123,6 +123,7 @@ pb:
|
||||
protoc -I protobuf/ protobuf/commonpb/common.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/sliverpb/sliver.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/clientpb/client.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/dnspb/dns.proto --go_out=paths=source_relative:protobuf/
|
||||
protoc -I protobuf/ protobuf/rpcpb/services.proto --go_out=paths=source_relative:protobuf/ --go-grpc_out=protobuf/ --go-grpc_opt=paths=source_relative
|
||||
|
||||
.PHONY: clean-all
|
||||
|
||||
@@ -20,9 +20,11 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
|
||||
* Local and remote process injection
|
||||
* Windows process migration
|
||||
* Windows user token manipulation
|
||||
* Anti-anti-anti-forensics
|
||||
* Anti-fingerprinting features
|
||||
* Let's Encrypt integration
|
||||
* In-memory .NET assembly execution
|
||||
* COFF/BOF in-memory loader
|
||||
* Much more!
|
||||
|
||||
### Getting Started
|
||||
|
||||
@@ -43,3 +45,4 @@ See the [wiki](https://github.com/BishopFox/sliver/wiki/Compile-From-Source).
|
||||
### License - GPLv3
|
||||
|
||||
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
ver "github.com/bishopfox/sliver/client/version"
|
||||
@@ -101,6 +102,9 @@ func Setup(force bool, echo bool) {
|
||||
}
|
||||
saveAssetVersion(appDir)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(appDir, settingsFileName)); os.IsNotExist(err) {
|
||||
SaveSettings(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func setupCoffLoaderExt(appDir string) error {
|
||||
@@ -109,7 +113,7 @@ func setupCoffLoaderExt(appDir string) error {
|
||||
win64ExtDir := path.Join("windows", "amd64")
|
||||
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
|
||||
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
|
||||
manfiestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
|
||||
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
|
||||
loader64, err := assetsFs.ReadFile(coffLoader64)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -118,7 +122,7 @@ func setupCoffLoaderExt(appDir string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifest, err := assetsFs.ReadFile(manfiestPath)
|
||||
manifest, err := assetsFs.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ type ClientConfig struct {
|
||||
Operator string `json:"operator"` // This value is actually ignored for the most part (cert CN is used instead)
|
||||
LHost string `json:"lhost"`
|
||||
LPort int `json:"lport"`
|
||||
Token string `json:"token"`
|
||||
CACertificate string `json:"ca_certificate"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Certificate string `json:"certificate"`
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package assets
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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 (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
settingsFileName = "tui-settings.json"
|
||||
)
|
||||
|
||||
// ClientSettings - Client JSON config
|
||||
type ClientSettings struct {
|
||||
TableStyle string `json:"tables"`
|
||||
AutoAdult bool `json:"autoadult"`
|
||||
BeaconAutoResults bool `json:"beacon_autoresults"`
|
||||
}
|
||||
|
||||
// LoadSettings - Load the client settings from disk
|
||||
func LoadSettings() (*ClientSettings, error) {
|
||||
rootDir, _ := filepath.Abs(GetRootAppDir())
|
||||
data, err := ioutil.ReadFile(filepath.Join(rootDir, settingsFileName))
|
||||
if err != nil {
|
||||
return defaultSettings(), err
|
||||
}
|
||||
settings := defaultSettings()
|
||||
err = json.Unmarshal(data, settings)
|
||||
if err != nil {
|
||||
return defaultSettings(), err
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func defaultSettings() *ClientSettings {
|
||||
return &ClientSettings{
|
||||
TableStyle: "SliverDefault",
|
||||
AutoAdult: false,
|
||||
BeaconAutoResults: true,
|
||||
}
|
||||
}
|
||||
|
||||
// SaveSettings - Save the current settings to disk
|
||||
func SaveSettings(settings *ClientSettings) error {
|
||||
rootDir, _ := filepath.Abs(GetRootAppDir())
|
||||
if settings == nil {
|
||||
settings = defaultSettings()
|
||||
}
|
||||
data, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0600)
|
||||
return err
|
||||
}
|
||||
+2
-3
@@ -80,10 +80,9 @@ var rootCmd = &cobra.Command{
|
||||
|
||||
// StartClientConsole - Start the client console
|
||||
func StartClientConsole() error {
|
||||
assets.Setup(false, true)
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
fmt.Printf("No config files found at %s or -import\n", assets.GetConfigDir())
|
||||
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
|
||||
return nil
|
||||
}
|
||||
config := selectConfig()
|
||||
@@ -94,7 +93,7 @@ func StartClientConsole() error {
|
||||
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 %v", err)
|
||||
fmt.Printf("Connection to server failed %s", err)
|
||||
return nil
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
|
||||
// BackdoorCmd - Command to inject implant code into an existing binary
|
||||
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
backdoor, err := con.Rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
|
||||
FilePath: remoteFilePath,
|
||||
ProfileName: profileName,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Beacons
|
||||
========
|
||||
|
||||
This package implements the beacons command, which can be used to list and interact with beacons.
|
||||
@@ -0,0 +1,169 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// BeaconsCmd - Display/interact with beacons
|
||||
func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
killFlag := ctx.Flags.String("kill")
|
||||
killAll := ctx.Flags.Bool("kill-all")
|
||||
|
||||
// Handle kill
|
||||
if killFlag != "" {
|
||||
beacon, err := GetBeacon(con, killFlag)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = kill.KillBeacon(beacon, false, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
|
||||
}
|
||||
|
||||
if killAll {
|
||||
beacons, err := GetBeacons(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = kill.KillBeacon(beacon, true, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
|
||||
}
|
||||
}
|
||||
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
PrintBeacons(beacons.Beacons, con)
|
||||
}
|
||||
|
||||
// PrintBeacons - Display a list of beacons
|
||||
func PrintBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) {
|
||||
if len(beacons) == 0 {
|
||||
con.PrintInfof("No beacons 🙁\n")
|
||||
return
|
||||
}
|
||||
tw := renderBeacons(beacons, con)
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) table.Writer {
|
||||
width, _, err := terminal.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
if 182 < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Tasks",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Transport",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
}
|
||||
|
||||
for _, beacon := range beacons {
|
||||
color := console.Normal
|
||||
activeBeacon := con.ActiveTarget.GetBeacon()
|
||||
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
|
||||
color = console.Green
|
||||
}
|
||||
|
||||
nextCheckin := time.Unix(beacon.NextCheckin, 0)
|
||||
var next string
|
||||
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
|
||||
past := time.Now().Sub(nextCheckin)
|
||||
next = fmt.Sprintf("%s-%s%s", console.Bold+console.Red, past, console.Normal)
|
||||
} else {
|
||||
eta := nextCheckin.Sub(time.Now())
|
||||
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, eta, console.Normal)
|
||||
}
|
||||
if 182 < width {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
|
||||
fmt.Sprintf(color+"%d/%d"+console.Normal, beacon.TasksCountCompleted, beacon.TasksCount),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
})
|
||||
} else {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
})
|
||||
}
|
||||
}
|
||||
return tw
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"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"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoBeacons - No sessions available
|
||||
ErrNoBeacons = errors.New("no beacons")
|
||||
// ErrNoSelection - No selection made
|
||||
ErrNoSelection = errors.New("no selection")
|
||||
// ErrBeaconNotFound
|
||||
ErrBeaconNotFound = errors.New("no beacon found for this ID")
|
||||
)
|
||||
|
||||
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
|
||||
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(beacons.Beacons) == 0 {
|
||||
return nil, ErrNoBeacons
|
||||
}
|
||||
|
||||
beaconsMap := map[string]*clientpb.Beacon{}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
beaconsMap[beacon.ID] = beacon
|
||||
}
|
||||
keys := []string{}
|
||||
for beaconID := range beaconsMap {
|
||||
keys = append(keys, beaconID)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
for _, key := range keys {
|
||||
beacon := beaconsMap[key]
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
beacon.ID,
|
||||
beacon.Name,
|
||||
beacon.RemoteAddress,
|
||||
beacon.Hostname,
|
||||
beacon.Username,
|
||||
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
|
||||
)
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a beacon:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
survey.AskOne(prompt, &selected)
|
||||
if selected == "" {
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
// Go from the selected option -> index -> session
|
||||
for index, option := range options {
|
||||
if option == selected {
|
||||
return beaconsMap[keys[index]], nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoSelection
|
||||
}
|
||||
|
||||
func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(beacons.Beacons) == 0 {
|
||||
return nil, ErrNoBeacons
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
if beacon.ID == beaconID {
|
||||
return beacon, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrBeaconNotFound
|
||||
}
|
||||
|
||||
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(beacons.Beacons) == 0 {
|
||||
return nil, ErrNoBeacons
|
||||
}
|
||||
return beacons, nil
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"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"))
|
||||
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)
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
pruneBeacons := []*clientpb.Beacon{}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
nextCheckin := time.Unix(beacon.NextCheckin, 0)
|
||||
if time.Now().Before(nextCheckin) {
|
||||
continue
|
||||
}
|
||||
delta := time.Now().Sub(nextCheckin)
|
||||
if pruneDuration <= delta {
|
||||
pruneBeacons = append(pruneBeacons, beacon)
|
||||
}
|
||||
}
|
||||
if len(pruneBeacons) == 0 {
|
||||
con.PrintInfof("No beacons to prune.\n")
|
||||
return
|
||||
}
|
||||
con.PrintWarnf("The following beacons and their tasks will be removed:\n")
|
||||
for index, beacon := range pruneBeacons {
|
||||
beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
con.Printf("\t%d. %s (%s)\n", (index + 1), beacon.Name, beacon.ID)
|
||||
}
|
||||
con.Println()
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Prune these beacons?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
errCount := 0
|
||||
for _, beacon := range pruneBeacons {
|
||||
_, err := con.Rpc.RmBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
errCount++
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Pruned %d beacon(s)\n", len(pruneBeacons)-errCount)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// BeaconsRmCmd - Display/interact with beacons
|
||||
func BeaconsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
beacon, err := SelectBeacon(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
_, err = con.Rpc.RmBeacon(context.Background(), beacon)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Beacon removed (%s)\n", beacon.ID)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package beacons
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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) {
|
||||
done := waitForInput()
|
||||
defer func() {
|
||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||
con.Printf(console.UpN+console.Clearln+"\r", 1)
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
panic(err) // If we return we may leak the waiting goroutine, so we panic instead
|
||||
}
|
||||
tw := renderBeacons(beacons.Beacons, con)
|
||||
lines := strings.Split(tw.Render(), "\n")
|
||||
for _, line := range lines {
|
||||
con.Printf(console.Clearln+"\r%s\n", line)
|
||||
}
|
||||
con.Printf("\nPress enter to stop.\n")
|
||||
con.Printf(console.UpN+"\r", len(lines)+2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func waitForInput() <-chan bool {
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
defer close(done)
|
||||
fmt.Scanf("\n")
|
||||
done <- true
|
||||
}()
|
||||
return done
|
||||
}
|
||||
+446
-35
@@ -36,8 +36,11 @@ package command
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/socks"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/backdoor"
|
||||
"github.com/bishopfox/sliver/client/command/beacons"
|
||||
"github.com/bishopfox/sliver/client/command/dllhijack"
|
||||
"github.com/bishopfox/sliver/client/command/environment"
|
||||
"github.com/bishopfox/sliver/client/command/exec"
|
||||
@@ -48,6 +51,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/hosts"
|
||||
"github.com/bishopfox/sliver/client/command/info"
|
||||
"github.com/bishopfox/sliver/client/command/jobs"
|
||||
"github.com/bishopfox/sliver/client/command/kill"
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/command/macros"
|
||||
"github.com/bishopfox/sliver/client/command/monitor"
|
||||
@@ -55,14 +59,18 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/operators"
|
||||
"github.com/bishopfox/sliver/client/command/pivots"
|
||||
"github.com/bishopfox/sliver/client/command/portfwd"
|
||||
operator "github.com/bishopfox/sliver/client/command/prelude-operator"
|
||||
"github.com/bishopfox/sliver/client/command/privilege"
|
||||
"github.com/bishopfox/sliver/client/command/processes"
|
||||
"github.com/bishopfox/sliver/client/command/reaction"
|
||||
"github.com/bishopfox/sliver/client/command/registry"
|
||||
"github.com/bishopfox/sliver/client/command/screenshot"
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/command/shell"
|
||||
"github.com/bishopfox/sliver/client/command/tasks"
|
||||
"github.com/bishopfox/sliver/client/command/update"
|
||||
"github.com/bishopfox/sliver/client/command/use"
|
||||
"github.com/bishopfox/sliver/client/command/websites"
|
||||
"github.com/bishopfox/sliver/client/command/wireguard"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -184,8 +192,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("l", "lport", generate.DefaultWGLPort, "udp listen port")
|
||||
f.Int("n", "nport", generate.DefaultWGNPort, "virtual tun interface listen port")
|
||||
f.Int("x", "key-port", generate.DefaultWGKeyExPort, "virtual tun interface key exchange port")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("p", "persistent", false, "make persistent across restarts")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -227,6 +236,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("w", "website", "", "website name (see websites cmd)")
|
||||
f.String("L", "lhost", "", "interface to bind server to")
|
||||
f.Int("l", "lport", generate.DefaultHTTPLPort, "tcp listen port")
|
||||
f.Bool("D", "disable-otp", false, "disable otp authentication")
|
||||
f.String("T", "long-poll-timeout", "1m30s", "server-side long poll timeout")
|
||||
f.String("J", "long-poll-jitter", "30s", "server-side long poll jitter")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("p", "persistent", false, "make persistent across restarts")
|
||||
@@ -249,10 +261,12 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("w", "website", "", "website name (see websites cmd)")
|
||||
f.String("L", "lhost", "", "interface to bind server to")
|
||||
f.Int("l", "lport", generate.DefaultHTTPSLPort, "tcp listen port")
|
||||
f.Bool("D", "disable-otp", false, "disable otp authentication")
|
||||
f.String("T", "long-poll-timeout", "1m30s", "server-side long poll timeout")
|
||||
f.String("J", "long-poll-jitter", "30s", "server-side long poll jitter")
|
||||
|
||||
f.String("c", "cert", "", "PEM encoded certificate file")
|
||||
f.String("k", "key", "", "PEM encoded private key file")
|
||||
|
||||
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -290,9 +304,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
// [ Operators ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.PlayersStr,
|
||||
Name: consts.OperatorsStr,
|
||||
Help: "Manage operators",
|
||||
LongHelp: help.GetHelpFor([]string{consts.PlayersStr}),
|
||||
LongHelp: help.GetHelpFor([]string{consts.OperatorsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -381,32 +395,13 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.UseStr,
|
||||
Help: "Switch the active session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.UseStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("session", "session ID or name", grumble.Default(""))
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sessions.UseCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.KillStr,
|
||||
Help: "Kill a session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.KillStr}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sessions.KillCmd(ctx, con)
|
||||
kill.KillCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
@@ -418,6 +413,200 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
openSessionCmd := &grumble.Command{
|
||||
Name: consts.InteractiveStr,
|
||||
Help: "Task a beacon to open an interactive session (Beacon only)",
|
||||
LongHelp: help.GetHelpFor([]string{consts.InteractiveStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("m", "mtls", "", "mtls connection strings")
|
||||
f.String("g", "wg", "", "wg connection strings")
|
||||
f.String("b", "http", "", "http(s) connection strings")
|
||||
f.String("n", "dns", "", "dns connection strings")
|
||||
f.String("p", "named-pipe", "", "named-pipe connection strings")
|
||||
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
|
||||
|
||||
f.String("d", "delay", "0s", "delay opening the session (after checkin) for a given period of time")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sessions.InteractiveCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
}
|
||||
con.App.AddCommand(openSessionCmd)
|
||||
|
||||
// [ Tasks ] --------------------------------------------------------------
|
||||
|
||||
tasksCmd := &grumble.Command{
|
||||
Name: consts.TasksStr,
|
||||
Help: "Beacon task management",
|
||||
LongHelp: help.GetHelpFor([]string{consts.TasksStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
|
||||
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
|
||||
f.String("f", "filter", "", "filter based on task type (case-insensitive prefix matching)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
tasks.TasksCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
}
|
||||
tasksCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.FetchStr,
|
||||
Help: "Fetch the details of a beacon task",
|
||||
LongHelp: help.GetHelpFor([]string{consts.TasksStr, consts.FetchStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
|
||||
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
|
||||
f.String("f", "filter", "", "filter based on task type (case-insensitive prefix matching)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("id", "beacon task ID", grumble.Default(""))
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
tasks.TasksFetchCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
con.App.AddCommand(tasksCmd)
|
||||
|
||||
// [ Use ] --------------------------------------------------------------
|
||||
|
||||
useCmd := &grumble.Command{
|
||||
Name: consts.UseStr,
|
||||
Help: "Switch the active session or beacon",
|
||||
LongHelp: help.GetHelpFor([]string{consts.UseStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("id", "beacon or session ID", grumble.Default(""))
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
use.UseCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
Completer: func(prefix string, args []string) []string {
|
||||
return use.Completer(con, prefix, args)
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
}
|
||||
useCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.SessionsStr,
|
||||
Help: "Switch the active session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.UseStr, consts.SessionsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
use.UseSessionCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
useCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.BeaconsStr,
|
||||
Help: "Switch the active beacon",
|
||||
LongHelp: help.GetHelpFor([]string{consts.UseStr, consts.BeaconsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
use.UseBeaconCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
con.App.AddCommand(useCmd)
|
||||
|
||||
// [ Settings ] --------------------------------------------------------------
|
||||
|
||||
settingsCmd := &grumble.Command{
|
||||
Name: consts.SettingsStr,
|
||||
Help: "Manage client settings",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SettingsStr}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
settings.SettingsCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
}
|
||||
settingsCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.SaveStr,
|
||||
Help: "Save the current settings to disk",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, consts.SaveStr}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
settings.SettingsSaveCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
settingsCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.TablesStr,
|
||||
Help: "Modify tables setting (style)",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, consts.TablesStr}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
settings.SettingsTablesCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
settingsCmd.AddCommand(&grumble.Command{
|
||||
Name: "beacon-autoresults",
|
||||
Help: "Automatically display beacon task results when completed",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "beacon-autoresults"}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
settings.SettingsBeaconsAutoResultCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
settingsCmd.AddCommand(&grumble.Command{
|
||||
Name: "autoadult",
|
||||
Help: "Automatically accept OPSEC warnings",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "autoadult"}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
settings.SettingsAutoAdultCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
con.App.AddCommand(settingsCmd)
|
||||
|
||||
// [ Info ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
@@ -548,8 +737,11 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
LongHelp: help.GetHelpFor([]string{consts.ExecuteStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("T", "token", false, "execute command with current token (windows only)")
|
||||
f.Bool("s", "silent", false, "don't print the command output")
|
||||
f.Bool("o", "output", false, "capture command output")
|
||||
f.Bool("X", "loot", false, "save output as loot")
|
||||
f.Bool("S", "ignore-stderr", false, "don't print STDERR output")
|
||||
f.String("O", "stdout", "", "remote path to redirect STDOUT to")
|
||||
f.String("E", "stderr", "", "remote path to redirect STDERR to")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -789,13 +981,13 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("N", "name", "", "agent name")
|
||||
f.Bool("d", "debug", false, "enable debug features")
|
||||
f.Bool("e", "evasion", false, "enable evasion features")
|
||||
f.Bool("b", "skip-symbols", false, "skip symbol obfuscation")
|
||||
f.Bool("l", "skip-symbols", false, "skip symbol obfuscation")
|
||||
|
||||
f.String("c", "canary", "", "canary domain(s)")
|
||||
|
||||
f.String("m", "mtls", "", "mtls connection strings")
|
||||
f.String("g", "wg", "", "wg connection strings")
|
||||
f.String("H", "http", "", "http(s) connection strings")
|
||||
f.String("b", "http", "", "http(s) connection strings")
|
||||
f.String("n", "dns", "", "dns connection strings")
|
||||
f.String("p", "named-pipe", "", "named-pipe connection strings")
|
||||
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
|
||||
@@ -804,7 +996,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll", generate.DefaultPoll, "attempt to poll every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
|
||||
f.String("w", "limit-datetime", "", "limit execution to before datetime")
|
||||
@@ -814,7 +1006,6 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
|
||||
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
|
||||
|
||||
f.String("s", "save", "", "directory/file to the binary to")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -827,6 +1018,59 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
}
|
||||
generateCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.BeaconStr,
|
||||
Help: "Generate a beacon binary",
|
||||
LongHelp: help.GetHelpFor([]string{consts.GenerateStr, consts.BeaconStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int64("D", "days", 0, "beacon interval days")
|
||||
f.Int64("H", "hours", 0, "beacon interval hours")
|
||||
f.Int64("M", "minutes", 0, "beacon interval minutes")
|
||||
f.Int64("S", "seconds", 60, "beacon interval seconds")
|
||||
f.Int64("J", "jitter", 30, "beacon interval jitter in seconds")
|
||||
|
||||
// Generate flags
|
||||
f.String("o", "os", "windows", "operating system")
|
||||
f.String("a", "arch", "amd64", "cpu architecture")
|
||||
f.String("N", "name", "", "agent name")
|
||||
f.Bool("d", "debug", false, "enable debug features")
|
||||
f.Bool("e", "evasion", false, "enable evasion features")
|
||||
f.Bool("l", "skip-symbols", false, "skip symbol obfuscation")
|
||||
|
||||
f.String("c", "canary", "", "canary domain(s)")
|
||||
|
||||
f.String("m", "mtls", "", "mtls connection strings")
|
||||
f.String("g", "wg", "", "wg connection strings")
|
||||
f.String("b", "http", "", "http(s) connection strings")
|
||||
f.String("n", "dns", "", "dns connection strings")
|
||||
f.String("p", "named-pipe", "", "named-pipe connection strings")
|
||||
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
|
||||
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
|
||||
f.String("w", "limit-datetime", "", "limit execution to before datetime")
|
||||
f.Bool("x", "limit-domainjoined", false, "limit execution to domain joined machines")
|
||||
f.String("y", "limit-username", "", "limit execution to specified username")
|
||||
f.String("z", "limit-hostname", "", "limit execution to specified hostname")
|
||||
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
|
||||
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
|
||||
f.String("s", "save", "", "directory/file to the binary to")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
generate.GenerateBeaconCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
generateCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.StagerStr,
|
||||
Help: "Generate a implant stager using MSF",
|
||||
@@ -946,7 +1190,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
f.Int("P", "poll", generate.DefaultPoll, "attempt to poll every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "attempt to poll every n second(s)")
|
||||
|
||||
f.String("w", "limit-datetime", "", "limit execution to before datetime")
|
||||
f.Bool("x", "limit-domainjoined", false, "limit execution to domain joined machines")
|
||||
@@ -956,7 +1200,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
|
||||
|
||||
f.String("N", "profile-name", "", "profile name")
|
||||
f.String("R", "profile-name", "", "profile name")
|
||||
f.String("N", "name", "", "agent name")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1024,9 +1269,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
con.App.AddCommand(implantBuildsCmd)
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ListCanariesStr,
|
||||
Name: consts.CanariesStr,
|
||||
Help: "List previously generated canaries",
|
||||
LongHelp: help.GetHelpFor([]string{consts.ListCanariesStr}),
|
||||
LongHelp: help.GetHelpFor([]string{consts.CanariesStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("b", "burned", false, "show only triggered/burned canaries")
|
||||
|
||||
@@ -1049,6 +1294,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
LongHelp: help.GetHelpFor([]string{consts.LsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("r", "reverse", false, "reverse sort order")
|
||||
f.Bool("m", "modified", false, "sort by modified time")
|
||||
f.Bool("s", "size", false, "sort by size")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("path", "path to enumerate", grumble.Default("."))
|
||||
@@ -1211,6 +1459,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Help: "View network interface configurations",
|
||||
LongHelp: help.GetHelpFor([]string{consts.IfconfigStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("A", "all", false, "show all network adapters (default only shows IPv4)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
@@ -1254,6 +1504,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("e", "exe", "", "filter based on executable name")
|
||||
f.String("o", "owner", "", "filter based on owner")
|
||||
f.Bool("c", "print-cmdline", false, "print command line arguments")
|
||||
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
|
||||
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1273,6 +1525,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("p", "pid", -1, "target pid")
|
||||
f.String("n", "name", "", "target process name")
|
||||
f.String("s", "save", "", "save to file (will overwrite if exists)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
@@ -1503,6 +1757,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Help: "Take a screenshot",
|
||||
LongHelp: help.GetHelpFor([]string{consts.ScreenshotStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("s", "save", "", "save to file (will overwrite if exists)")
|
||||
f.Bool("X", "loot", false, "save output as loot")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -1538,6 +1793,74 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
})
|
||||
|
||||
// [ Beacons ] ---------------------------------------------
|
||||
|
||||
beaconsCmd := &grumble.Command{
|
||||
Name: consts.BeaconsStr,
|
||||
Help: "Manage beacons",
|
||||
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("k", "kill", "", "kill a beacon")
|
||||
f.Bool("K", "kill-all", false, "kill all beacons")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
beacons.BeaconsCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
beaconsCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RmStr,
|
||||
Help: "Remove a beacon",
|
||||
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr, consts.RmStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
HelpGroup: consts.SliverWinHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
beacons.BeaconsRmCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
beaconsCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.WatchStr,
|
||||
Help: "Watch your beacons",
|
||||
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr, consts.WatchStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
HelpGroup: consts.SliverWinHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
beacons.BeaconsWatchCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
beaconsCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.PruneStr,
|
||||
Help: "Prune stale beacons automatically",
|
||||
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr, consts.PruneStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("d", "duration", "1h", "duration to prune beacons that have missed their last checkin")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
HelpGroup: consts.SliverWinHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
beacons.BeaconsPruneCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
con.App.AddCommand(beaconsCmd)
|
||||
|
||||
// [ Macros ] ---------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
@@ -1965,6 +2288,59 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
})
|
||||
con.App.AddCommand(portfwdCmd)
|
||||
|
||||
// [ Socks ] --------------------------------------------------------------
|
||||
|
||||
socksCmd := &grumble.Command{
|
||||
Name: consts.Socks5Str,
|
||||
Help: "In-band SOCKS5 Proxy",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "router timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
socks.SocksCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
}
|
||||
socksCmd.AddCommand(&grumble.Command{
|
||||
Name: "add",
|
||||
Help: "Create a new Socks5 Proxy",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("H", "host", "127.0.0.1", "Bind a Socks5 Host")
|
||||
f.String("P", "port", "1081", "Bind a Socks5 Port")
|
||||
f.String("u", "user", "", "socks5 auth user")
|
||||
f.String("p", "password", "", "socks5 auth pass")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
socks.SocksAddCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
socksCmd.AddCommand(&grumble.Command{
|
||||
Name: "rm",
|
||||
Help: "Remove a Socks5 Proxy",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "router timeout in seconds")
|
||||
f.Int("i", "id", 0, "id of portfwd to remove")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
socks.SocksRmCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
con.App.AddCommand(socksCmd)
|
||||
|
||||
// [ Monitor ] --------------------------------------------------------------
|
||||
|
||||
monitorCmd := &grumble.Command{
|
||||
@@ -2089,9 +2465,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
lootCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.LootFetchStr,
|
||||
Name: consts.FetchStr,
|
||||
Help: "Fetch a piece of loot from the server's loot store",
|
||||
LongHelp: help.GetHelpFor([]string{consts.LootStr, consts.LootFetchStr}),
|
||||
LongHelp: help.GetHelpFor([]string{consts.LootStr, consts.FetchStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("s", "save", "", "save loot to a local file")
|
||||
f.String("f", "filter", "", "filter based on loot type")
|
||||
@@ -2338,4 +2714,39 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
})
|
||||
con.App.AddCommand(extensionCmd)
|
||||
// [ Prelude's Operator ] ------------------------------------------------------------
|
||||
operatorCmd := &grumble.Command{
|
||||
Name: consts.PreludeOperatorStr,
|
||||
Help: "Manage connection to Prelude's Operator",
|
||||
LongHelp: help.GetHelpFor([]string{consts.PreludeOperatorStr}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
operator.OperatorCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
operatorCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.ConnectStr,
|
||||
Help: "Connect with Prelude's Operator",
|
||||
LongHelp: help.GetHelpFor([]string{consts.PreludeOperatorStr, consts.ConnectStr}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
operator.ConnectCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("connection-string", "connection string to the Operator Host")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("s", "skip-existing", false, "Do not add existing sessions as Operator Agents")
|
||||
f.String("a", "aes-key", "abcdefghijklmnopqrstuvwxyz012345", "AES key for communication encryption")
|
||||
f.String("r", "range", "sliver", "Agents range")
|
||||
},
|
||||
})
|
||||
|
||||
con.App.AddCommand(operatorCmd)
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
targetDLLData []byte
|
||||
err error
|
||||
)
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -78,12 +78,12 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Crafting and planting DLL at %s ...", targetPath)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err = con.Rpc.HijackDLL(context.Background(), &sliverpb.DllHijackReq{
|
||||
_, err = con.Rpc.HijackDLL(context.Background(), &clientpb.DllHijackReq{
|
||||
ReferenceDLLPath: referencePath,
|
||||
TargetLocation: targetPath,
|
||||
ReferenceDLL: localRefData,
|
||||
TargetDLL: targetDLLData,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
ProfileName: profileName,
|
||||
})
|
||||
ctrl <- true
|
||||
|
||||
@@ -22,28 +22,49 @@ import (
|
||||
"context"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.Get()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Args.String("name")
|
||||
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if envInfo.Response != nil && envInfo.Response.Async {
|
||||
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, envInfo)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintGetEnvInfo(envInfo, con)
|
||||
})
|
||||
con.PrintAsyncResponse(envInfo.Response)
|
||||
} else {
|
||||
PrintGetEnvInfo(envInfo, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintGetEnvInfo - Print the results of the env get command
|
||||
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
}
|
||||
for _, envVar := range envInfo.Variables {
|
||||
con.Printf("%s=%s\n", envVar.Key, envVar.Value)
|
||||
}
|
||||
|
||||
@@ -22,15 +22,17 @@ import (
|
||||
"context"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.Get()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,12 +48,30 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Key: name,
|
||||
Value: value,
|
||||
},
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if envInfo.Response != nil && envInfo.Response.Async {
|
||||
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, envInfo)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSetEnvInfo(name, value, envInfo, con)
|
||||
})
|
||||
con.PrintAsyncResponse(envInfo.Response)
|
||||
} else {
|
||||
PrintSetEnvInfo(name, value, envInfo, con)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PrintSetEnvInfo - Print the set environment info
|
||||
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
|
||||
@@ -22,14 +22,16 @@ import (
|
||||
"context"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.Get()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -41,16 +43,33 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
|
||||
Name: name,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if unsetResp.Response != nil && unsetResp.Response.Async {
|
||||
con.AddBeaconCallback(unsetResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, unsetResp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintUnsetEnvInfo(name, unsetResp, con)
|
||||
})
|
||||
con.PrintAsyncResponse(unsetResp.Response)
|
||||
} else {
|
||||
PrintUnsetEnvInfo(name, unsetResp, con)
|
||||
}
|
||||
|
||||
if unsetResp.Response != nil && unsetResp.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", unsetResp.Response.Err)
|
||||
}
|
||||
|
||||
// PrintUnsetEnvInfo - Print the set environment info
|
||||
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) {
|
||||
if envInfo.Response != nil && envInfo.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", envInfo.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully unset %s\n", name)
|
||||
|
||||
@@ -3,14 +3,17 @@ package exec
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
@@ -20,26 +23,27 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
|
||||
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := ctx.Args.String("filepath")
|
||||
assemblyPath := ctx.Args.String("filepath")
|
||||
isDLL := false
|
||||
if filepath.Ext(filePath) == ".dll" {
|
||||
if filepath.Ext(assemblyPath) == ".dll" {
|
||||
isDLL = true
|
||||
}
|
||||
if isDLL {
|
||||
@@ -48,7 +52,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
}
|
||||
assemblyBytes, err := ioutil.ReadFile(filePath)
|
||||
assemblyBytes, err := ioutil.ReadFile(assemblyPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
@@ -59,8 +63,8 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
executeAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: strings.Join(assemblyArgs, " "),
|
||||
@@ -72,30 +76,50 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v", err)
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
hostname := getHostname(session, beacon)
|
||||
if execAssembly.Response != nil && execAssembly.Response.Async {
|
||||
con.AddBeaconCallback(execAssembly.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, execAssembly)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(execAssembly.Response)
|
||||
} else {
|
||||
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExecuteAssembly - Print the results of an assembly execution
|
||||
func PrintExecuteAssembly(execAssembly *sliverpb.ExecuteAssembly, hostname string,
|
||||
assemblyPath string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
if execAssembly.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
if executeAssembly.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Error: %s\n", executeAssembly.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
var outFilePath *os.File
|
||||
var err error
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
}
|
||||
con.PrintInfof("Assembly output:\n%s", string(executeAssembly.GetOutput()))
|
||||
con.PrintInfof("Assembly output:\n%s", string(execAssembly.GetOutput()))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(executeAssembly.GetOutput())
|
||||
outFilePath.Write(execAssembly.GetOutput())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
|
||||
if ctx.Flags.Bool("loot") && 0 < len(executeAssembly.GetOutput()) {
|
||||
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(filePath))
|
||||
err = loot.AddLootFile(con.Rpc, name, "console.txt", executeAssembly.GetOutput(), false)
|
||||
if ctx.Flags.Bool("loot") && 0 < len(execAssembly.GetOutput()) {
|
||||
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(assemblyPath))
|
||||
err = loot.AddLootFile(con.Rpc, name, "console.txt", execAssembly.GetOutput(), false)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save output as loot: %s\n", err)
|
||||
} else {
|
||||
@@ -103,3 +127,13 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.Hostname
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.Hostname
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -29,20 +29,26 @@ import (
|
||||
|
||||
"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/crypto/ssh/terminal"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ExecuteShellcodeCmd - Execute shellcode in-memory
|
||||
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
interactive := ctx.Flags.Bool("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")
|
||||
shellcodeBin, err := ioutil.ReadFile(shellcodePath)
|
||||
@@ -61,11 +67,11 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
|
||||
con.SpinUntil(msg, ctrl)
|
||||
task, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Data: shellcodeBin,
|
||||
RWXPages: ctx.Flags.Bool("rwx-pages"),
|
||||
Pid: uint32(pid),
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -73,16 +79,34 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if shellcodeTask.Response != nil && shellcodeTask.Response.Async {
|
||||
con.AddBeaconCallback(shellcodeTask.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, shellcodeTask)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExecuteShellcode(shellcodeTask, con)
|
||||
})
|
||||
con.PrintAsyncResponse(shellcodeTask.Response)
|
||||
} else {
|
||||
PrintExecuteShellcode(shellcodeTask, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExecuteShellcode - Display result of shellcode execution
|
||||
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||
if task.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", task.Response.GetErr())
|
||||
return
|
||||
} else {
|
||||
con.PrintInfof("Executed shellcode on target\n")
|
||||
}
|
||||
con.PrintInfof("Executed shellcode on target\n")
|
||||
}
|
||||
|
||||
func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
|
||||
// Check active session
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -97,21 +121,21 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
|
||||
|
||||
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: hostProc,
|
||||
EnablePTY: !noPty,
|
||||
TunnelID: tunnel.ID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v\n", err)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// Retrieve PID and start remote task
|
||||
@@ -121,7 +145,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.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: pid,
|
||||
Data: shellcode,
|
||||
RWXPages: rwxPages,
|
||||
@@ -130,7 +154,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %v", err)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -22,58 +22,122 @@ import (
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cmdPath := ctx.Args.String("command")
|
||||
args := ctx.Args.StringList("arguments")
|
||||
output := ctx.Flags.Bool("silent")
|
||||
token := ctx.Flags.Bool("token")
|
||||
output := ctx.Flags.Bool("output")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
|
||||
if output && beacon != nil {
|
||||
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
|
||||
}
|
||||
|
||||
var exec *sliverpb.Execute
|
||||
var err error
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token {
|
||||
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: !output,
|
||||
Output: output,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
})
|
||||
} else {
|
||||
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: !output,
|
||||
Output: output,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
})
|
||||
}
|
||||
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
} else if !output {
|
||||
if exec.Status != 0 {
|
||||
con.PrintErrorf("Exited with status %d!\n", exec.Status)
|
||||
if exec.Result != "" {
|
||||
con.PrintInfof("Output:\n%s\n", exec.Result)
|
||||
return
|
||||
}
|
||||
|
||||
if exec.Response != nil && exec.Response.Async {
|
||||
con.AddBeaconCallback(exec.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, exec)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExecute(exec, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(exec.Response)
|
||||
} else {
|
||||
PrintExecute(exec, ctx, 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")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
cmdPath := ctx.Args.String("command")
|
||||
args := ctx.Args.StringList("arguments")
|
||||
|
||||
output := ctx.Flags.Bool("output")
|
||||
if !output {
|
||||
if exec.Status == 0 {
|
||||
con.PrintInfof("Command executed successfully\n")
|
||||
} else {
|
||||
con.PrintInfof("Output:\n%s\n", exec.Result)
|
||||
if ctx.Flags.Bool("loot") && 0 < len(exec.Result) {
|
||||
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
|
||||
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(exec.Result), false)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save output as loot: %s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Output saved as loot\n")
|
||||
}
|
||||
}
|
||||
con.PrintErrorf("Exit code %d\n", exec.Status)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
combined := ""
|
||||
if stdout == "" {
|
||||
combined = exec.Stdout
|
||||
con.PrintInfof("Output:\n%s", exec.Stdout)
|
||||
} else {
|
||||
con.PrintInfof("Stdout saved at %s\n", stdout)
|
||||
}
|
||||
|
||||
if stderr == "" {
|
||||
if !ignoreStderr && 0 < len(exec.Stderr) {
|
||||
combined = fmt.Sprintf("%s\nStderr:\n%s", combined, exec.Stderr)
|
||||
con.PrintInfof("Stderr:\n%s", exec.Stderr)
|
||||
}
|
||||
} else {
|
||||
con.PrintInfof("Stderr saved at %s\n", stderr)
|
||||
}
|
||||
|
||||
if exec.Status != 0 {
|
||||
con.PrintErrorf("Exited with status %d!\n", exec.Status)
|
||||
}
|
||||
if saveLoot && 0 < len(combined) {
|
||||
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
|
||||
err := loot.AddLootFile(con.Rpc, name, "console.txt", []byte(combined), false)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save output as loot: %s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Output saved as loot\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
// MigrateCmd - Windows only, inject an implant into another process
|
||||
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.Get()
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
||||
Pid: uint32(pid),
|
||||
Config: config,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -25,13 +25,15 @@ import (
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -57,8 +59,8 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
msg := fmt.Sprintf("Injecting payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, session.OS, session.Arch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
@@ -70,7 +72,24 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if msfTask.Response != nil && msfTask.Response.Async {
|
||||
con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, msfTask)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintMsfRemote(msfTask, con)
|
||||
})
|
||||
con.PrintAsyncResponse(msfTask.Response)
|
||||
} else {
|
||||
con.PrintInfof("Executed payload on target\n")
|
||||
PrintMsfRemote(msfTask, con)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
|
||||
// MsfCmd - Inject a metasploit payload into the current remote process
|
||||
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
payloadName, session.OS, session.Arch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
LPort: uint32(lport),
|
||||
|
||||
@@ -21,11 +21,12 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
insecureRand "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"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"
|
||||
@@ -36,14 +37,14 @@ import (
|
||||
|
||||
// PsExecCmd - psexec command implementation.
|
||||
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
if hostname == "" {
|
||||
con.PrintWarnf("you need to provide a target host, see `help psexec` for examples")
|
||||
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
|
||||
return
|
||||
}
|
||||
profile := ctx.Flags.String("profile")
|
||||
@@ -53,9 +54,9 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
|
||||
|
||||
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
|
||||
con.PrintWarnf("Warning: You're going to deploy the following service:\n- Name: %s\n- Description: %s\n", serviceName, serviceDesc)
|
||||
con.PrintWarnf("You're going to deploy the following service:\n- Name: %s\n- Description: %s\n", serviceName, serviceDesc)
|
||||
con.PrintWarnf("You might want to change that before going further...\n")
|
||||
if !con.IsUserAnAdult() {
|
||||
if !settings.IsUserAnAdult(con) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -96,7 +97,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Encoder: "gzip",
|
||||
Data: uploadGzip,
|
||||
Path: filePath,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
uploadCtrl <- true
|
||||
<-uploadCtrl
|
||||
@@ -119,7 +120,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
|
||||
BinPath: binaryPath,
|
||||
Hostname: hostname,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
ServiceDescription: serviceDesc,
|
||||
ServiceName: serviceName,
|
||||
Arguments: "",
|
||||
@@ -142,7 +143,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Hostname: hostname,
|
||||
ServiceName: serviceName,
|
||||
},
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
removeChan <- true
|
||||
<-removeChan
|
||||
@@ -159,10 +160,9 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
func randomString(length int) string {
|
||||
var charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[seededRand.Intn(len(charset))]
|
||||
b[i] = charset[insecureRand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
@@ -3,14 +3,17 @@ package exec
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
@@ -20,19 +23,20 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.Get()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,7 +55,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Args: args,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
@@ -66,14 +70,38 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
hostname := getHostname(session, beacon)
|
||||
if sideload.Response != nil && sideload.Response.Async {
|
||||
con.AddBeaconCallback(sideload.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, sideload)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSideload(sideload, hostname, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideload.Response)
|
||||
} else {
|
||||
PrintSideload(sideload, hostname, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSideload - Print the sideload command output
|
||||
func PrintSideload(sideload *sliverpb.Sideload, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if sideload.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Error: %s\n", sideload.GetResponse().GetErr())
|
||||
con.PrintErrorf("%s\n", sideload.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
var err error
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Output:\n%s", sideload.GetResult())
|
||||
if outFilePath != nil {
|
||||
|
||||
@@ -5,18 +5,20 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.Get()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
dllArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
|
||||
@@ -36,24 +38,48 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
Args: dllArgs,
|
||||
EntryPoint: exportName,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
hostname := getHostname(session, beacon)
|
||||
if spawndll.Response != nil && spawndll.Response.Async {
|
||||
con.AddBeaconCallback(spawndll.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, spawndll)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSpawnDll(spawndll, hostname, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawndll.Response)
|
||||
} else {
|
||||
PrintSpawnDll(spawndll, hostname, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSpawnDll - Print the SpawnDll command response
|
||||
func PrintSpawnDll(spawndll *sliverpb.SpawnDll, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if spawndll.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("Error: %s\n", spawndll.GetResponse().GetErr())
|
||||
con.PrintErrorf("Failed to spawn dll: %s\n", spawndll.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
var err error
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Output:\n%s", spawndll.GetResult())
|
||||
if outFilePath != nil {
|
||||
|
||||
+30
-13
@@ -11,6 +11,7 @@ import (
|
||||
"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)
|
||||
@@ -19,7 +20,7 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
privKey []byte
|
||||
err error
|
||||
)
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -51,33 +52,49 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
commandResp, err := con.Rpc.RunSSHCommand(context.Background(), &sliverpb.SSHCommandReq{
|
||||
sshCmd, err := con.Rpc.RunSSHCommand(context.Background(), &sliverpb.SSHCommandReq{
|
||||
Username: username,
|
||||
Hostname: hostname,
|
||||
Port: uint32(port),
|
||||
PrivKey: privKey,
|
||||
Password: password,
|
||||
Command: strings.Join(command, " "),
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if sshCmd.Response != nil && sshCmd.Response.Async {
|
||||
con.AddBeaconCallback(sshCmd.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, sshCmd)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSSHCmd(sshCmd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sshCmd.Response)
|
||||
} else {
|
||||
PrintSSHCmd(sshCmd, con)
|
||||
}
|
||||
}
|
||||
|
||||
if commandResp.Response != nil && commandResp.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", commandResp.Response.Err)
|
||||
if commandResp.StdErr != "" {
|
||||
con.PrintErrorf("StdErr: %s\n", commandResp.StdErr)
|
||||
// PrintSSHCmd - Print the ssh command response
|
||||
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
|
||||
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
|
||||
if sshCmd.StdErr != "" {
|
||||
con.PrintErrorf("StdErr: %s\n", sshCmd.StdErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if commandResp.StdOut != "" {
|
||||
con.PrintInfof("Output:")
|
||||
con.Println(commandResp.StdOut)
|
||||
if commandResp.StdErr != "" {
|
||||
con.PrintInfof("StdErr")
|
||||
con.Println(commandResp.StdErr)
|
||||
if sshCmd.StdOut != "" {
|
||||
con.PrintInfof("Output:\n")
|
||||
con.Println(sshCmd.StdOut)
|
||||
if sshCmd.StdErr != "" {
|
||||
con.PrintInfof("StdErr:\n")
|
||||
con.Println(sshCmd.StdErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package extensions
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -13,10 +12,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
@@ -59,9 +57,9 @@ func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, tar
|
||||
for _, ef := range e.Files {
|
||||
if targetOS == ef.OS {
|
||||
switch targetArch {
|
||||
case "x86":
|
||||
case "386":
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
|
||||
case "x64":
|
||||
case "amd64":
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
|
||||
default:
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
|
||||
@@ -78,7 +76,6 @@ func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dirPath := ctx.Args.String("dir-path")
|
||||
extCmds, err := ParseExtensions(dirPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -159,13 +156,13 @@ func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConso
|
||||
}
|
||||
|
||||
func ListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s", err)
|
||||
@@ -193,7 +190,7 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
|
||||
// Try to find the extension in the loaded extensions
|
||||
if len(session.Extensions) == 0 {
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err.Error())
|
||||
@@ -256,7 +253,7 @@ func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand,
|
||||
Data: binData,
|
||||
OS: session.OS,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -291,7 +288,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
extName string
|
||||
entryPoint string
|
||||
)
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -303,7 +300,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if err = loadExtension(ctx, session, con, &ext); err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
con.PrintErrorf("Could not load extension: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -325,7 +322,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
isBOF := filepath.Ext(binPath) == ".o"
|
||||
|
||||
// BOFs (Beacon Object Files) are a specific kind of extensions
|
||||
// than require another extension (a COFF loader) to be present.
|
||||
// that require another extension (a COFF loader) to be present.
|
||||
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
|
||||
// This block will pack both the BOF data and its arguments into a single buffer that
|
||||
// the loader will extract and load.
|
||||
@@ -352,7 +349,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Name: extName,
|
||||
Export: entryPoint,
|
||||
Args: extensionArgs,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -380,7 +377,7 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]b
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argsBuffer := BOFArgsBuffer{
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
// Parse BOF arguments from grumble
|
||||
@@ -429,7 +426,7 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]b
|
||||
return nil, err
|
||||
}
|
||||
// Now build the extension's argument buffer
|
||||
extensionArgsBuffer := BOFArgsBuffer{
|
||||
extensionArgsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
}
|
||||
err = extensionArgsBuffer.AddString(ext.Entrypoint)
|
||||
@@ -464,64 +461,3 @@ func cmdExists(name string, app *grumble.App) bool {
|
||||
func init() {
|
||||
commandMap = make(map[string]extensionCommand)
|
||||
}
|
||||
|
||||
// BOF Specific code
|
||||
|
||||
type BOFArgsBuffer struct {
|
||||
Buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *BOFArgsBuffer) AddData(d []byte) error {
|
||||
dataLen := uint32(len(d))
|
||||
err := binary.Write(b.Buffer, binary.LittleEndian, &dataLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Write(b.Buffer, binary.LittleEndian, &d)
|
||||
}
|
||||
|
||||
func (b *BOFArgsBuffer) AddShort(d uint16) error {
|
||||
return binary.Write(b.Buffer, binary.LittleEndian, &d)
|
||||
}
|
||||
|
||||
func (b *BOFArgsBuffer) AddInt(d uint32) error {
|
||||
return binary.Write(b.Buffer, binary.LittleEndian, &d)
|
||||
}
|
||||
|
||||
func (b *BOFArgsBuffer) AddString(d string) error {
|
||||
stringLen := uint32(len(d)) + 1
|
||||
err := binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dBytes := append([]byte(d), 0x00)
|
||||
return binary.Write(b.Buffer, binary.LittleEndian, dBytes)
|
||||
}
|
||||
|
||||
func (b *BOFArgsBuffer) AddWString(d string) error {
|
||||
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
|
||||
strBytes := append([]byte(d), 0x00)
|
||||
utf16Data, err := encoder.Bytes(strBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stringLen := uint32(len(utf16Data))
|
||||
err = binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Write(b.Buffer, binary.LittleEndian, utf16Data)
|
||||
}
|
||||
|
||||
func (b *BOFArgsBuffer) GetBuffer() ([]byte, error) {
|
||||
outBuffer := new(bytes.Buffer)
|
||||
err := binary.Write(outBuffer, binary.LittleEndian, uint32(b.Buffer.Len()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(outBuffer, binary.LittleEndian, b.Buffer.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outBuffer.Bytes(), nil
|
||||
}
|
||||
@@ -28,16 +28,18 @@ import (
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,13 +50,35 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
})
|
||||
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, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintCat(download, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCat - Print the download to stdout
|
||||
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
return
|
||||
}
|
||||
if download.Encoder == "gzip" {
|
||||
download.Data, err = new(encoders.Gzip).Decode(download.Data)
|
||||
if err != nil {
|
||||
@@ -73,14 +97,6 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Println(string(download.Data))
|
||||
}
|
||||
}
|
||||
// if ctx.Flags.Bool("loot") && 0 < len(download.Data) {
|
||||
// err = AddLootFile(rpc, fmt.Sprintf("[cat] %s", filepath.Base(filePath)), filePath, download.Data, false)
|
||||
// if err != nil {
|
||||
// con.PrintErrorf("Failed to save output as loot: %s", err)
|
||||
// } else {
|
||||
// fmt.Printf(clearln + Info + "Output saved as loot\n")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func colorize(f *sliverpb.Download) error {
|
||||
|
||||
@@ -22,26 +22,40 @@ import (
|
||||
"context"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
filePath := ctx.Args.String("path")
|
||||
|
||||
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if pwd.Response != nil && pwd.Response.Async {
|
||||
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, pwd)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintPwd(pwd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(pwd.Response)
|
||||
} else {
|
||||
con.PrintInfof("%s\n", pwd.Path)
|
||||
PrintPwd(pwd, con)
|
||||
}
|
||||
}
|
||||
@@ -27,16 +27,18 @@ import (
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"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"
|
||||
)
|
||||
|
||||
// DownloadCmd - Download a file from the remote system
|
||||
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,19 +57,10 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dst = path.Join(dst, fileName)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
overwrite := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite local file?"}
|
||||
survey.AskOne(prompt, &overwrite, nil)
|
||||
if !overwrite {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
})
|
||||
ctrl <- true
|
||||
@@ -76,7 +69,50 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
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
|
||||
}
|
||||
PrintDownload(download, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(download.Response)
|
||||
} else {
|
||||
PrintDownload(download, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintDownload - Print the download response, and save file to disk
|
||||
func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
localPath := ctx.Args.String("local-path")
|
||||
|
||||
src := remotePath
|
||||
fileName := filepath.Base(src)
|
||||
dst, _ := filepath.Abs(localPath)
|
||||
fi, err := os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
dst = path.Join(dst, fileName)
|
||||
}
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
overwrite := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite local file?"}
|
||||
survey.AskOne(prompt, &overwrite, nil)
|
||||
if !overwrite {
|
||||
return
|
||||
}
|
||||
}
|
||||
if download.Encoder == "gzip" {
|
||||
download.Data, err = new(encoders.Gzip).Decode(download.Data)
|
||||
if err != nil {
|
||||
@@ -96,13 +132,4 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
} else {
|
||||
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
|
||||
}
|
||||
|
||||
// if ctx.Flags.Bool("loot") && 0 < len(download.Data) {
|
||||
// err = AddLootFile(rpc, fmt.Sprintf("[download] %s", filepath.Base(remotePath)), remotePath, download.Data, false)
|
||||
// if err != nil {
|
||||
// con.PrintErrorf("Failed to save output as loot: %s", err)
|
||||
// } else {
|
||||
// fmt.Printf(Info + "Output saved as loot\n")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -22,48 +22,179 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("path")
|
||||
filter := ""
|
||||
|
||||
/*
|
||||
Check to see if the remote path is a filter or contains a filter.
|
||||
If the path passes the test to be a filter, then it is a filter
|
||||
because paths are not valid filters.
|
||||
*/
|
||||
if remotePath != "." {
|
||||
|
||||
// Check if the path contains a filter
|
||||
// Test on a standardized version of the path (change any \ to /)
|
||||
testPath := strings.Replace(remotePath, "\\", "/", -1)
|
||||
/*
|
||||
Cannot use the path or filepath libraries because the OS
|
||||
of the client does not necessarily match the OS of the
|
||||
implant
|
||||
*/
|
||||
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
|
||||
|
||||
if lastSeparatorOccurrence == -1 {
|
||||
// Then this is only a filter
|
||||
filter = remotePath
|
||||
remotePath = "."
|
||||
} else {
|
||||
// Then we need to test for a filter on the end of the string
|
||||
|
||||
// The indicies should be the same because we did not change the length of the string
|
||||
baseDir := remotePath[:lastSeparatorOccurrence+1]
|
||||
potentialFilter := remotePath[lastSeparatorOccurrence+1:]
|
||||
|
||||
_, err := filepath.Match(potentialFilter, "")
|
||||
|
||||
if err == nil {
|
||||
// Then we have a filter on the end of the path
|
||||
remotePath = baseDir
|
||||
filter = potentialFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if ls.Response != nil && ls.Response.Async {
|
||||
con.AddBeaconCallback(ls.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, ls)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintLs(ls, ctx.Flags, filter, con)
|
||||
})
|
||||
con.PrintAsyncResponse(ls.Response)
|
||||
} else {
|
||||
PrintLs(ls, con)
|
||||
PrintLs(ls, ctx.Flags, filter, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintLs - Display an sliverpb.Ls object
|
||||
func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
|
||||
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, filter string, con *console.SliverConsoleClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
con.Printf("%s\n", ls.Path)
|
||||
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
for _, fileInfo := range ls.Files {
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t<dir>\t\n", fileInfo.Name)
|
||||
|
||||
// Extract the flags
|
||||
reverseSort := flags.Bool("reverse")
|
||||
sortByTime := flags.Bool("modified")
|
||||
sortBySize := flags.Bool("size")
|
||||
|
||||
/*
|
||||
By default, name sorting is case sensitive. Upper case entries come before
|
||||
lower case ones. Instead, we will level the playing field and sort
|
||||
regardless of case
|
||||
*/
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return strings.ToLower(ls.Files[i].Name) > strings.ToLower(ls.Files[j].Name)
|
||||
})
|
||||
} else {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return strings.ToLower(ls.Files[i].Name) < strings.ToLower(ls.Files[j].Name)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
After names are sorted properly, take care of the modified time if the
|
||||
user wants to sort by time. Doing this after sorting by name
|
||||
will make the time sorted entries properly sorted by name if times are equal.
|
||||
*/
|
||||
|
||||
if sortByTime {
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].ModTime > ls.Files[j].ModTime
|
||||
})
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t\n", fileInfo.Name, util.ByteCountBinary(fileInfo.Size))
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].ModTime < ls.Files[j].ModTime
|
||||
})
|
||||
}
|
||||
} else if sortBySize {
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].Size > ls.Files[j].Size
|
||||
})
|
||||
} else {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].Size < ls.Files[j].Size
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, fileInfo := range ls.Files {
|
||||
if filter != "" {
|
||||
fileMatch, err := filepath.Match(filter, fileInfo.Name)
|
||||
|
||||
if err != nil {
|
||||
/*
|
||||
This should not happen because we checked the filter
|
||||
before reaching out to the implant.
|
||||
*/
|
||||
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
|
||||
break
|
||||
}
|
||||
|
||||
if !fileMatch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
modTime := time.Unix(fileInfo.ModTime, 0)
|
||||
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
|
||||
modTime = modTime.In(implantLocation)
|
||||
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
@@ -22,15 +22,17 @@ import (
|
||||
"context"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -42,12 +44,33 @@ func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if mkdir.Response != nil && mkdir.Response.Async {
|
||||
con.AddBeaconCallback(mkdir.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, mkdir)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintMkdir(mkdir, con)
|
||||
})
|
||||
con.PrintAsyncResponse(mkdir.Response)
|
||||
} else {
|
||||
con.PrintInfof("%s\n", mkdir.Path)
|
||||
PrintMkdir(mkdir, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMkdir - Print make directory
|
||||
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverConsoleClient) {
|
||||
if mkdir.Response != nil && mkdir.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", mkdir.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", mkdir.Path)
|
||||
}
|
||||
@@ -22,24 +22,46 @@ import (
|
||||
"context"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pwd, err := con.Rpc.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if pwd.Response != nil && pwd.Response.Async {
|
||||
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, pwd)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintPwd(pwd, con)
|
||||
})
|
||||
con.PrintAsyncResponse(pwd.Response)
|
||||
} else {
|
||||
con.PrintInfof("%s\n", pwd.Path)
|
||||
PrintPwd(pwd, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintPwd - Print the remote working directory
|
||||
func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverConsoleClient) {
|
||||
if pwd.Response != nil && pwd.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", pwd.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", pwd.Path)
|
||||
}
|
||||
@@ -22,15 +22,17 @@ import (
|
||||
"context"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -42,14 +44,35 @@ func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: filePath,
|
||||
Recursive: ctx.Flags.Bool("recursive"),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if rm.Response != nil && rm.Response.Async {
|
||||
con.AddBeaconCallback(rm.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, rm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRm(rm, con)
|
||||
})
|
||||
con.PrintAsyncResponse(rm.Response)
|
||||
} else {
|
||||
con.PrintInfof("%s\n", rm.Path)
|
||||
PrintRm(rm, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRm - Print the rm response
|
||||
func PrintRm(rm *sliverpb.Rm, con *console.SliverConsoleClient) {
|
||||
if rm.Response != nil && rm.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", rm.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s\n", rm.Path)
|
||||
}
|
||||
@@ -26,16 +26,18 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"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) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,7 +69,7 @@ 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.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: dst,
|
||||
Data: uploadGzip,
|
||||
Encoder: "gzip",
|
||||
@@ -77,7 +79,28 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if upload.Response != nil && upload.Response.Async {
|
||||
con.AddBeaconCallback(upload.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, upload)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintUpload(upload, con)
|
||||
})
|
||||
con.PrintAsyncResponse(upload.Response)
|
||||
} else {
|
||||
con.PrintInfof("Wrote file to %s\n", upload.Path)
|
||||
PrintUpload(upload, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintUpload - Print the result of the upload command
|
||||
func PrintUpload(upload *sliverpb.Upload, con *console.SliverConsoleClient) {
|
||||
if upload.Response != nil && upload.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", upload.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Wrote file to %s\n", upload.Path)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
minBeaconInterval = 5 * time.Second
|
||||
ErrBeaconIntervalTooShort = fmt.Errorf("Beacon interval must be %v or greater", minBeaconInterval)
|
||||
)
|
||||
|
||||
// GenerateBeaconCmd - The main command used to generate implant binaries
|
||||
func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
config := parseCompileFlags(ctx, con)
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
config.IsBeacon = true
|
||||
err := parseBeaconFlags(ctx, con, config)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
save := ctx.Flags.String("save")
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
compile(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
|
||||
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
|
||||
if interval < minBeaconInterval {
|
||||
return ErrBeaconIntervalTooShort
|
||||
}
|
||||
config.BeaconInterval = int64(interval)
|
||||
config.BeaconJitter = int64(time.Duration(ctx.Flags.Int64("jitter")) * time.Second)
|
||||
return nil
|
||||
}
|
||||
@@ -62,8 +62,8 @@ const (
|
||||
|
||||
// DefaultReconnect is the default reconnect time
|
||||
DefaultReconnect = 60
|
||||
// DefaultPoll is the default poll interval
|
||||
DefaultPoll = 1
|
||||
// DefaultPollTimeout is the default poll timeout
|
||||
DefaultPollTimeout = 360 // 6 minutes
|
||||
// DefaultMaxErrors is the default max reconnection errors before giving up
|
||||
DefaultMaxErrors = 1000
|
||||
)
|
||||
@@ -82,31 +82,6 @@ var (
|
||||
"windows/386": true,
|
||||
"windows/amd64": true,
|
||||
}
|
||||
|
||||
// validFormats = []string{
|
||||
// "bash",
|
||||
// "c",
|
||||
// "csharp",
|
||||
// "dw",
|
||||
// "dword",
|
||||
// "hex",
|
||||
// "java",
|
||||
// "js_be",
|
||||
// "js_le",
|
||||
// "num",
|
||||
// "perl",
|
||||
// "pl",
|
||||
// "powershell",
|
||||
// "ps1",
|
||||
// "py",
|
||||
// "python",
|
||||
// "raw",
|
||||
// "rb",
|
||||
// "ruby",
|
||||
// "sh",
|
||||
// "vbapplication",
|
||||
// "vbscript",
|
||||
// }
|
||||
)
|
||||
|
||||
// GenerateCmd - The main command used to generate implant binaries
|
||||
@@ -193,22 +168,22 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
|
||||
mtlsC2 := parseMTLSc2(ctx.Flags.String("mtls"))
|
||||
mtlsC2 := ParseMTLSc2(ctx.Flags.String("mtls"))
|
||||
c2s = append(c2s, mtlsC2...)
|
||||
|
||||
wgC2 := parseWGc2(ctx.Flags.String("wg"))
|
||||
wgC2 := ParseWGc2(ctx.Flags.String("wg"))
|
||||
c2s = append(c2s, wgC2...)
|
||||
|
||||
httpC2 := parseHTTPc2(ctx.Flags.String("http"))
|
||||
httpC2 := ParseHTTPc2(ctx.Flags.String("http"))
|
||||
c2s = append(c2s, httpC2...)
|
||||
|
||||
dnsC2 := parseDNSc2(ctx.Flags.String("dns"))
|
||||
dnsC2 := ParseDNSc2(ctx.Flags.String("dns"))
|
||||
c2s = append(c2s, dnsC2...)
|
||||
|
||||
namedPipeC2 := parseNamedPipec2(ctx.Flags.String("named-pipe"))
|
||||
namedPipeC2 := ParseNamedPipec2(ctx.Flags.String("named-pipe"))
|
||||
c2s = append(c2s, namedPipeC2...)
|
||||
|
||||
tcpPivotC2 := parseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
|
||||
tcpPivotC2 := ParseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
|
||||
c2s = append(c2s, tcpPivotC2...)
|
||||
|
||||
var symbolObfuscation bool
|
||||
@@ -235,7 +210,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
}
|
||||
|
||||
reconnectInterval := ctx.Flags.Int("reconnect")
|
||||
pollInterval := ctx.Flags.Int("poll")
|
||||
pollTimeout := ctx.Flags.Int("poll-timeout")
|
||||
maxConnectionErrors := ctx.Flags.Int("max-errors")
|
||||
|
||||
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
|
||||
@@ -308,8 +283,8 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
|
||||
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
|
||||
|
||||
ReconnectInterval: uint32(reconnectInterval),
|
||||
PollInterval: uint32(pollInterval),
|
||||
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
|
||||
PollTimeout: int64(pollTimeout) * int64(time.Second),
|
||||
MaxConnectionErrors: uint32(maxConnectionErrors),
|
||||
|
||||
LimitDomainJoined: limitDomainJoined,
|
||||
@@ -349,11 +324,11 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
|
||||
|
||||
target := fmt.Sprintf("%s/%s", targetOS, targetArch)
|
||||
if _, ok := SupportedCompilerTargets[target]; !ok {
|
||||
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a Default implant.\n",
|
||||
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a generic implant.\n",
|
||||
console.Bold, target, console.Normal,
|
||||
)
|
||||
con.Printf("⚠️ Default implants do not support all commands/features.\n")
|
||||
prompt := &survey.Confirm{Message: "Compile a Default build?"}
|
||||
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
|
||||
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
@@ -364,7 +339,8 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
|
||||
return targetOS, targetArch
|
||||
}
|
||||
|
||||
func parseMTLSc2(args string) []*clientpb.ImplantC2 {
|
||||
// ParseMTLSc2 - Parse mtls connection string arg
|
||||
func ParseMTLSc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
@@ -383,7 +359,8 @@ func parseMTLSc2(args string) []*clientpb.ImplantC2 {
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseWGc2(args string) []*clientpb.ImplantC2 {
|
||||
// ParseWGc2 - Parse wg connect string arg
|
||||
func ParseWGc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
@@ -403,7 +380,8 @@ func parseWGc2(args string) []*clientpb.ImplantC2 {
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseHTTPc2(args string) []*clientpb.ImplantC2 {
|
||||
// ParseHTTPc2 - Parse HTTP connection string arg
|
||||
func ParseHTTPc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
@@ -433,7 +411,8 @@ func parseHTTPc2(args string) []*clientpb.ImplantC2 {
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseDNSc2(args string) []*clientpb.ImplantC2 {
|
||||
// ParseDNSc2 - Parse DNS connection string arg
|
||||
func ParseDNSc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
@@ -458,7 +437,8 @@ func parseDNSc2(args string) []*clientpb.ImplantC2 {
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseNamedPipec2(args string) []*clientpb.ImplantC2 {
|
||||
// ParseNamedPipec2 - Parse named pipe connection string arg
|
||||
func ParseNamedPipec2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
@@ -479,7 +459,8 @@ func parseNamedPipec2(args string) []*clientpb.ImplantC2 {
|
||||
return c2s
|
||||
}
|
||||
|
||||
func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
|
||||
// ParseTCPPivotc2 - Parse tcp pivot connection string arg
|
||||
func ParseTCPPivotc2(args string) []*clientpb.ImplantC2 {
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
if args == "" {
|
||||
return c2s
|
||||
@@ -500,9 +481,12 @@ func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
|
||||
}
|
||||
|
||||
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
|
||||
|
||||
con.PrintInfof("Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
|
||||
|
||||
if config.IsBeacon {
|
||||
interval := time.Duration(config.BeaconInterval)
|
||||
con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
|
||||
} else {
|
||||
con.PrintInfof("Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
|
||||
}
|
||||
if config.ObfuscateSymbols {
|
||||
con.PrintInfof("%sSymbol obfuscation is enabled%s\n", console.Bold, console.Normal)
|
||||
} else if !config.Debug {
|
||||
@@ -572,7 +556,7 @@ func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string
|
||||
|
||||
compilers, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintWarnf("Failed to check target compatibility: %s\n", err)
|
||||
con.PrintErrorf("Failed to check target compatibility: %s\n", err)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -607,7 +591,7 @@ func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossC
|
||||
}
|
||||
|
||||
func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
|
||||
con.PrintWarnf("WARNING: Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch)
|
||||
con.PrintWarnf("Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch)
|
||||
switch targetOS {
|
||||
case "windows":
|
||||
con.PrintWarnf("The server cannot find an installation of mingw")
|
||||
|
||||
@@ -45,8 +45,15 @@ func printHelp(con *console.SliverConsoleClient) {
|
||||
groups := make(map[string]*grumble.Commands)
|
||||
for _, c := range con.App.Commands().All() {
|
||||
key := c.HelpGroup
|
||||
if con.ActiveSession.Get() != nil {
|
||||
if con.ActiveSession.Get().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
|
||||
targetOS := ""
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session != nil {
|
||||
targetOS = session.OS
|
||||
} else if beacon != nil {
|
||||
targetOS = beacon.OS
|
||||
}
|
||||
if beacon != nil || session != nil {
|
||||
if targetOS != "windows" && key == consts.SliverWinHelpGroup {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -173,7 +173,7 @@ Execution limits can be used to restrict the execution of a Sliver implant to ma
|
||||
|
||||
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
|
||||
Due to the large number of options and C2s this can be a lot of typing. If you'd like to have a reusable a Sliver config
|
||||
see 'help new-profile'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
|
||||
see 'help profiles new'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
|
||||
command.
|
||||
`
|
||||
generateStagerHelp = `[[.Bold]]Command:[[.Normal]] generate stager <options>
|
||||
@@ -201,23 +201,23 @@ When a stager calls back to this URL, a sliver corresponding to the said profile
|
||||
|
||||
stage-listener --url tcp://1.2.3.4:8080 --profile my-sliver-profile
|
||||
|
||||
To create a profile, use the [[.Bold]]new-profile[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
|
||||
To create a profile, use the [[.Bold]]profiles new[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
|
||||
|
||||
new-profile --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
|
||||
profiles new --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
|
||||
`
|
||||
|
||||
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new [--profile-name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Create a new profile with a given name and options, a name is required.
|
||||
|
||||
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
|
||||
Profiles are an easy way to save a sliver configurate and easily generate multiple copies of the binary with the same
|
||||
settings, but will still have per-binary certificates/obfuscation/etc. This command is used with generate-profile:
|
||||
new-profile --profile-name mtls-profile --mtls foo.example.com --canary 1.foobar.com
|
||||
Profiles are an easy way to save an implant configuration and easily generate multiple copies of the binary with the same
|
||||
settings. Generated implants will still have per-binary certificates/obfuscation/etc. This command is used with generate-profile:
|
||||
profiles new --profile-name mtls-profile --mtls foo.example.com --canary 1.foobar.com
|
||||
generate-profile mtls-profile
|
||||
`
|
||||
|
||||
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate-profile [name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Generate a Sliver from a saved profile (see new-profile).`
|
||||
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
|
||||
|
||||
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
|
||||
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
|
||||
@@ -228,21 +228,45 @@ settings, but will still have per-binary certificates/obfuscation/etc. This comm
|
||||
psHelp = `[[.Bold]]Command:[[.Normal]] ps <options>
|
||||
[[.Bold]]About:[[.Normal]] List processes on remote system.`
|
||||
|
||||
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <sliver name/session>
|
||||
[[.Bold]]About:[[.Normal]] Ping Sliver by name or the active sliver. This does NOT send an ICMP packet, it just sends an empty
|
||||
c2 message round trip to ensure the remote Sliver is still responding to commands.`
|
||||
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <implant name/session>
|
||||
[[.Bold]]About:[[.Normal]] Ping session by name or the active session. This does NOT send an ICMP packet, it just sends a small
|
||||
C2 message round trip to ensure the remote implant is still responding to commands.`
|
||||
|
||||
killHelp = `[[.Bold]]Command:[[.Normal]] kill <sliver name/session>
|
||||
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
|
||||
killHelp = `[[.Bold]]Command:[[.Normal]] kill <implant name/session>
|
||||
[[.Bold]]About:[[.Normal]] Kill a remote implant process (does not delete file).`
|
||||
|
||||
lsHelp = `[[.Bold]]Command:[[.Normal]] ls <remote path>
|
||||
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.`
|
||||
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.
|
||||
|
||||
[[.Bold]][[.Underline]]Sorting[[.Normal]]
|
||||
Directory and file listings are sorted by name in ascending order by default. Listings can also be sorted by size (-s) and modified time (-m). All sorts can be reversed with -r.
|
||||
|
||||
[[.Bold]][[.Underline]]Filters[[.Normal]]
|
||||
Filters are a way to limit search results to directory and file names matching given criteria.
|
||||
|
||||
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
|
||||
/etc/passwd will display the listing for /etc/passwd. "/etc/" is the path, and "passwd" is the filter.
|
||||
|
||||
Directory and file listings can be filtered using the following patterns:
|
||||
'*': Wildcard, matches any sequence of non-path separators (slashes)
|
||||
Example: n*.txt will match all file and directory names starting with n and ending with .txt
|
||||
|
||||
'?': Single character wildcard, matches a single non-path separator (slashes)
|
||||
Example: s?iver will match all file and directory names starting with s followed by any non-separator character and ending with iver.
|
||||
|
||||
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
|
||||
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all directory and file names that start with a, b, or c.
|
||||
^[r-u] will match all characters except r, s, t, and u.
|
||||
|
||||
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
|
||||
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
|
||||
`
|
||||
|
||||
cdHelp = `[[.Bold]]Command:[[.Normal]] cd [remote path]
|
||||
[[.Bold]]About:[[.Normal]] Change working directory of the active Sliver.`
|
||||
[[.Bold]]About:[[.Normal]] Change working directory of the active session.`
|
||||
|
||||
pwdHelp = `[[.Bold]]Command:[[.Normal]] pwd
|
||||
[[.Bold]]About:[[.Normal]] Print working directory of the active Sliver.`
|
||||
[[.Bold]]About:[[.Normal]] Print working directory of the active session.`
|
||||
|
||||
mkdirHelp = `[[.Bold]]Command:[[.Normal]] mkdir [remote path]
|
||||
[[.Bold]]About:[[.Normal]] Create a remote directory.`
|
||||
@@ -266,20 +290,20 @@ c2 message round trip to ensure the remote Sliver is still responding to command
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Run a new process in the context of the designated user`
|
||||
|
||||
impersonateHelp = `[[.Bold]]Command:[[.Normal]] impersonate USERNAME
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that runs new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that run new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
|
||||
|
||||
revToSelfHelp = `[[.Bold]]Command:[[.Normal]] rev2self
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Call RevertToSelf, lose the stolen token.`
|
||||
|
||||
elevateHelp = `[[.Bold]]Command:[[.Normal]] elevate
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new sliver session as an elevated process (UAC bypass)`
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new Sliver session as an elevated process (UAC bypass)`
|
||||
|
||||
executeAssemblyHelp = `[[.Bold]]Command:[[.Normal]] execute-assembly [local path to assembly] [arguments]
|
||||
[[.Bold]]About:[[.Normal]] (Windows Only) Executes the .NET assembly in a child process.
|
||||
`
|
||||
|
||||
executeShellcodeHelp = `[[.Bold]]Command:[[.Normal]] execute-shellcode [local path to raw shellcode]
|
||||
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the Sliver process.
|
||||
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the implant's process.
|
||||
|
||||
[[.Bold]][[.Underline]]++ Shellcode ++[[.Normal]]
|
||||
Shellcode files should be binary encoded, you can generate Sliver shellcode files with the generate command:
|
||||
@@ -404,9 +428,9 @@ Each command will have the [[.Bold]]--process[[.Normal]] flag defined, which all
|
||||
This command uploads a Sliver binary generated on the fly from a profile.
|
||||
The profile must be created with the [[.Bold]]service[[.Normal]] format, so that the service manager can properly start and stop the binary.
|
||||
|
||||
To create such a profile, use the [[.Bold]]new-profile[[.Normal]] command:
|
||||
To create such a profile, use the [[.Bold]]profiles new[[.Normal]] command:
|
||||
|
||||
new-profile --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
|
||||
profiles new --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
|
||||
|
||||
Once the profile has been created, run the [[.Bold]]psexec[[.Normal]] command:
|
||||
|
||||
@@ -418,7 +442,7 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
|
||||
[[.Bold]]About:[[.Normal]] Inject a sliver shellcode into an existing file on the target system.
|
||||
[[.Bold]]Example:[[.Normal]] backdoor --profile windows-shellcode "c:\windows\system32\calc.exe"
|
||||
|
||||
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: new-profile --format shellcode --profile-name whatever --http ab.cd
|
||||
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --profile-name whatever --http ab.cd
|
||||
`
|
||||
makeTokenHelp = `[[.Bold]]Command:[[.Normal]] make-token -u USERNAME -d DOMAIN -p PASSWORD
|
||||
[[.Bold]]About:[[.Normal]] Creates a new Logon Session from the specified credentials and impersonate the resulting token.
|
||||
@@ -630,7 +654,7 @@ supplied with the --profile flag.
|
||||
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --file /tmp/blah.dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
|
||||
|
||||
# Use a Sliver generated DLL for the hijack
|
||||
new-profile --format shared --mtls 1.2.3.4:1234 --profile-name dll
|
||||
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll
|
||||
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
|
||||
|
||||
# Use a local DLL as the reference DLL
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"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"
|
||||
@@ -59,14 +60,15 @@ func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(table.StyleLight)
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Hostname",
|
||||
"Operating System",
|
||||
"Sessions",
|
||||
"Beacons",
|
||||
"IOCs",
|
||||
"Extension Data",
|
||||
"ID (Short)",
|
||||
"Extensions",
|
||||
})
|
||||
for _, host := range hosts {
|
||||
var shortID string
|
||||
@@ -76,12 +78,13 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
|
||||
shortID = host.HostUUID[:8]
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
shortID,
|
||||
host.Hostname,
|
||||
host.OSVersion,
|
||||
hostSessionNumbers(host.HostUUID, con),
|
||||
hostBeacons(host.HostUUID, con),
|
||||
len(host.IOCs),
|
||||
len(host.ExtensionData),
|
||||
shortID,
|
||||
})
|
||||
}
|
||||
return tw.Render()
|
||||
@@ -99,6 +102,25 @@ func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) strin
|
||||
return strings.Join(sessionNumbers, ", ")
|
||||
}
|
||||
|
||||
func hostBeacons(hostUUID string, con *console.SliverConsoleClient) string {
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return "Error"
|
||||
}
|
||||
count := 0
|
||||
for _, beacon := range beacons.Beacons {
|
||||
if beacon.UUID == hostUUID {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
return "None"
|
||||
} else {
|
||||
return fmt.Sprintf("%d", count)
|
||||
}
|
||||
}
|
||||
|
||||
// SessionsForHost - Find session for a given host by id
|
||||
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
|
||||
+52
-22
@@ -21,23 +21,18 @@ package info
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// InfoCmd - Display information about the active session
|
||||
func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
var session *clientpb.Session
|
||||
sessionName := ctx.Args.String("session")
|
||||
if con.ActiveSession.GetInteractive() != nil {
|
||||
session = con.ActiveSession.GetInteractive()
|
||||
} else if sessionName != "" {
|
||||
session = con.GetSession(sessionName)
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if session != nil {
|
||||
|
||||
con.Printf(console.Bold+" ID: %s%d\n", console.Normal, session.ID)
|
||||
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, session.Name)
|
||||
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, session.Hostname)
|
||||
@@ -51,8 +46,27 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, session.Arch)
|
||||
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, session.RemoteAddress)
|
||||
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, session.ProxyURL)
|
||||
con.Printf(console.Bold+" Poll Interval: %s%d\n", console.Normal, session.PollInterval)
|
||||
con.Printf(console.Bold+" Poll Timeout: %s%d\n", console.Normal, session.PollTimeout)
|
||||
con.Printf(console.Bold+"Reconnect Interval: %s%d\n", console.Normal, session.ReconnectInterval)
|
||||
|
||||
} else if beacon != nil {
|
||||
|
||||
con.Printf(console.Bold+" ID: %s%s\n", console.Normal, beacon.ID)
|
||||
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, beacon.Name)
|
||||
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, beacon.Hostname)
|
||||
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, beacon.UUID)
|
||||
con.Printf(console.Bold+" Username: %s%s\n", console.Normal, beacon.Username)
|
||||
con.Printf(console.Bold+" UID: %s%s\n", console.Normal, beacon.UID)
|
||||
con.Printf(console.Bold+" GID: %s%s\n", console.Normal, beacon.GID)
|
||||
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, beacon.PID)
|
||||
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, beacon.OS)
|
||||
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, beacon.Version)
|
||||
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, beacon.Arch)
|
||||
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, beacon.RemoteAddress)
|
||||
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, beacon.ProxyURL)
|
||||
con.Printf(console.Bold+" Interval: %s%d\n", console.Normal, beacon.Interval)
|
||||
con.Printf(console.Bold+" Jitter: %s%d\n", console.Normal, beacon.Jitter)
|
||||
|
||||
} else {
|
||||
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
|
||||
}
|
||||
@@ -60,36 +74,52 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// PIDCmd - Get the active session's PID
|
||||
func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
con.Printf("%d\n", session.PID)
|
||||
if session != nil {
|
||||
con.Printf("%d\n", session.PID)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%d\n", beacon.PID)
|
||||
}
|
||||
}
|
||||
|
||||
// UIDCmd - Get the active session's UID
|
||||
func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
con.Printf("%s\n", session.UID)
|
||||
if session != nil {
|
||||
con.Printf("%s\n", session.UID)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%s\n", beacon.UID)
|
||||
}
|
||||
}
|
||||
|
||||
// GIDCmd - Get the active session's GID
|
||||
func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
con.Printf("%s\n", session.GID)
|
||||
if session != nil {
|
||||
con.Printf("%s\n", session.GID)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%s\n", beacon.GID)
|
||||
}
|
||||
}
|
||||
|
||||
// WhoamiCmd - Displays the current user of the active session
|
||||
func WhoamiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
con.Printf("%s\n", session.Username)
|
||||
if session != nil {
|
||||
con.Printf("%s\n", session.Username)
|
||||
} else if beacon != nil {
|
||||
con.Printf("%s\n", beacon.Username)
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// PingCmd - Send a round trip C2 message to an implant (does not use ICMP)
|
||||
func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Ping %d\n", nonce)
|
||||
pong, err := con.Rpc.Ping(context.Background(), &sliverpb.Ping{
|
||||
Nonce: int32(nonce),
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -20,6 +20,7 @@ package jobs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -31,15 +32,29 @@ func HTTPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
domain := ctx.Flags.String("domain")
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
disableOTP := ctx.Flags.Bool("disable-otp")
|
||||
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Starting HTTP %s:%d listener ...\n", domain, lport)
|
||||
http, err := con.Rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{
|
||||
Domain: domain,
|
||||
Website: ctx.Flags.String("website"),
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Secure: false,
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
Domain: domain,
|
||||
Website: ctx.Flags.String("website"),
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Secure: false,
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
EnforceOTP: !disableOTP,
|
||||
LongPollTimeout: int64(longPollTimeout),
|
||||
LongPollJitter: int64(longPollJitter),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,6 +21,7 @@ package jobs
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -34,6 +35,18 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
disableOTP := ctx.Flags.Bool("disable-otp")
|
||||
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
cert, key, err := getLocalCertificatePair(ctx)
|
||||
if err != nil {
|
||||
con.Println()
|
||||
@@ -43,15 +56,18 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
con.PrintInfof("Starting HTTPS %s:%d listener ...\n", domain, lport)
|
||||
https, err := con.Rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{
|
||||
Domain: domain,
|
||||
Website: website,
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Secure: true,
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
ACME: ctx.Flags.Bool("lets-encrypt"),
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
Domain: domain,
|
||||
Website: website,
|
||||
Host: lhost,
|
||||
Port: uint32(lport),
|
||||
Secure: true,
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
ACME: ctx.Flags.Bool("lets-encrypt"),
|
||||
Persistent: ctx.Flags.Bool("persistent"),
|
||||
EnforceOTP: !disableOTP,
|
||||
LongPollTimeout: int64(longPollTimeout),
|
||||
LongPollJitter: int64(longPollJitter),
|
||||
})
|
||||
con.Println()
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package kill
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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"
|
||||
"errors"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"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"
|
||||
)
|
||||
|
||||
// KillCmd - Kill the active session (not to be confused with TerminateCmd)
|
||||
func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
// Confirm with the user, just in case they confused kill with terminate
|
||||
confirm := false
|
||||
promptSession := &survey.Confirm{Message: "Kill the active session?"}
|
||||
promptBeacon := &survey.Confirm{Message: "Kill the active beacon?"}
|
||||
if session != nil {
|
||||
survey.AskOne(promptSession, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
err := KillSession(session, ctx.Flags.Bool("force"), con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
|
||||
con.ActiveTarget.Background()
|
||||
return
|
||||
}
|
||||
if beacon != nil {
|
||||
survey.AskOne(promptBeacon, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
err := KillBeacon(beacon, ctx.Flags.Bool("force"), con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
|
||||
con.ActiveTarget.Background()
|
||||
return
|
||||
}
|
||||
con.PrintErrorf("No active session or beacon\n")
|
||||
return
|
||||
}
|
||||
|
||||
func KillSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
|
||||
if session == nil {
|
||||
return errors.New("session does not exist")
|
||||
}
|
||||
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Request: &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
},
|
||||
Force: force,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func KillBeacon(beacon *clientpb.Beacon, force bool, con *console.SliverConsoleClient) error {
|
||||
if beacon == nil {
|
||||
return errors.New("session does not exist")
|
||||
}
|
||||
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Request: &commonpb.Request{
|
||||
BeaconID: beacon.ID,
|
||||
},
|
||||
Force: force,
|
||||
})
|
||||
return err
|
||||
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func LootCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if filter == "" {
|
||||
allLoot, err = con.Rpc.LootAll(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintWarnf("Failed to fetch loot %s\n", err)
|
||||
con.PrintErrorf("Failed to fetch loot %s\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -33,7 +33,7 @@ import (
|
||||
|
||||
// LootAddRemoteCmd - Add a file from the remote system to the server as loot
|
||||
func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.SpinUntil(fmt.Sprintf("Looting remote file %s", remotePath), ctrl)
|
||||
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Extensions
|
||||
Macros
|
||||
===========
|
||||
|
||||
Wraps external DLLs/tool in Sliver console commands.
|
||||
@@ -185,7 +185,7 @@ func LoadMacroCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -246,7 +246,7 @@ func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
@@ -272,7 +272,7 @@ func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Args: strings.Trim(extArgs, " "),
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
@@ -297,7 +297,7 @@ func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Args: extArgs,
|
||||
Data: binData,
|
||||
EntryPoint: entryPoint,
|
||||
|
||||
@@ -20,37 +20,74 @@ package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// IfconfigCmd - Display network interfaces on the remote system
|
||||
func IfconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ifconfig, err := con.Rpc.Ifconfig(context.Background(), &sliverpb.IfconfigReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
all := ctx.Flags.Bool("all")
|
||||
if ifconfig.Response != nil && ifconfig.Response.Async {
|
||||
con.AddBeaconCallback(ifconfig.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, ifconfig)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintIfconfig(ifconfig, all, con)
|
||||
})
|
||||
con.PrintAsyncResponse(ifconfig.Response)
|
||||
} else {
|
||||
PrintIfconfig(ifconfig, all, con)
|
||||
}
|
||||
}
|
||||
|
||||
for ifaceIndex, iface := range ifconfig.NetInterfaces {
|
||||
con.Printf("%s%s%s (%d)\n", console.Bold, iface.Name, console.Normal, ifaceIndex)
|
||||
// PrintIfconfig - Print the ifconfig response
|
||||
func PrintIfconfig(ifconfig *sliverpb.Ifconfig, all bool, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
interfaces := ifconfig.NetInterfaces
|
||||
sort.Slice(interfaces, func(i, j int) bool {
|
||||
return interfaces[i].Index < interfaces[j].Index
|
||||
})
|
||||
hidden := 0
|
||||
for index, iface := range interfaces {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableWithBordersStyle(con))
|
||||
tw.SetTitle(fmt.Sprintf(console.Bold+"%s"+console.Normal, iface.Name))
|
||||
tw.SetColumnConfigs([]table.ColumnConfig{
|
||||
{Name: "#", AutoMerge: true},
|
||||
{Name: "IP Address", AutoMerge: true},
|
||||
{Name: "MAC Address", AutoMerge: true},
|
||||
})
|
||||
rowConfig := table.RowConfig{AutoMerge: true}
|
||||
tw.AppendHeader(table.Row{"#", "IP Addresses", "MAC Address"}, rowConfig)
|
||||
macAddress := ""
|
||||
if 0 < len(iface.MAC) {
|
||||
con.Printf(" MAC Address: %s\n", iface.MAC)
|
||||
macAddress = iface.MAC
|
||||
}
|
||||
ips := []string{}
|
||||
for _, ip := range iface.IPAddresses {
|
||||
|
||||
// Try to find local IPs and colorize them
|
||||
subnet := -1
|
||||
if strings.Contains(ip, "/") {
|
||||
@@ -61,15 +98,30 @@ func IfconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
subnet = -1
|
||||
}
|
||||
}
|
||||
|
||||
if 0 < subnet && subnet <= 32 && !isLoopback(ip) {
|
||||
con.Printf(console.Bold+console.Green+" IP Address: %s%s\n", ip, console.Normal)
|
||||
} else if 32 < subnet && !isLoopback(ip) {
|
||||
con.Printf(console.Bold+console.Cyan+" IP Address: %s%s\n", ip, console.Normal)
|
||||
} else {
|
||||
con.Printf(" IP Address: %s\n", ip)
|
||||
ips = append(ips, fmt.Sprintf(console.Bold+console.Green+"%s"+console.Normal, ip))
|
||||
} else if all {
|
||||
ips = append(ips, fmt.Sprintf("%s", ip))
|
||||
}
|
||||
}
|
||||
if !all && len(ips) < 1 {
|
||||
hidden++
|
||||
continue
|
||||
}
|
||||
if 0 < len(ips) {
|
||||
for _, ip := range ips {
|
||||
tw.AppendRow(table.Row{iface.Index, ip, macAddress}, rowConfig)
|
||||
}
|
||||
} else {
|
||||
tw.AppendRow(table.Row{iface.Index, " ", macAddress}, rowConfig)
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
if index+1 < len(interfaces) {
|
||||
con.Println()
|
||||
}
|
||||
}
|
||||
if !all {
|
||||
con.Printf("%d adapters not shown.\n", hidden)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,17 +22,20 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// NetstatCmd - Display active network connections on the remote system
|
||||
func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -42,8 +45,11 @@ func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
tcp := ctx.Flags.Bool("tcp")
|
||||
udp := ctx.Flags.Bool("udp")
|
||||
|
||||
implantPID := getPID(session, beacon)
|
||||
activeC2 := getActiveC2(session, beacon)
|
||||
|
||||
netstat, err := con.Rpc.Netstat(context.Background(), &sliverpb.NetstatReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
TCP: tcp,
|
||||
UDP: udp,
|
||||
Listening: listening,
|
||||
@@ -54,10 +60,22 @@ func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
displayEntries(netstat.Entries, con)
|
||||
if netstat.Response != nil && netstat.Response.Async {
|
||||
con.AddBeaconCallback(netstat.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, netstat)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintNetstat(netstat, implantPID, activeC2, con)
|
||||
})
|
||||
con.PrintAsyncResponse(netstat.Response)
|
||||
} else {
|
||||
PrintNetstat(netstat, implantPID, activeC2, con)
|
||||
}
|
||||
}
|
||||
|
||||
func displayEntries(entries []*sliverpb.SockTabEntry, con *console.SliverConsoleClient) {
|
||||
func PrintNetstat(netstat *sliverpb.Netstat, implantPID int32, activeC2 string, con *console.SliverConsoleClient) {
|
||||
lookup := func(skaddr *sliverpb.SockTabEntry_SockAddr) string {
|
||||
const IPv4Strlen = 17
|
||||
addr := skaddr.Ip
|
||||
@@ -71,30 +89,48 @@ func displayEntries(entries []*sliverpb.SockTabEntry, con *console.SliverConsole
|
||||
return fmt.Sprintf("%s:%d", addr, skaddr.Port)
|
||||
}
|
||||
|
||||
con.Printf("Proto %-23s %-23s %-12s %-16s\n", "Local Addr", "Foreign Addr", "State", "PID/Program name")
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
for _, e := range entries {
|
||||
p := ""
|
||||
if e.Process != nil {
|
||||
p = fmt.Sprintf("%d/%s", e.Process.Pid, e.Process.Executable)
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{"Protocol", "Local Address", "Foreign Address", "State", "PID/Program name"})
|
||||
|
||||
for _, entry := range netstat.Entries {
|
||||
pid := ""
|
||||
if entry.Process != nil {
|
||||
pid = fmt.Sprintf("%d/%s", entry.Process.Pid, entry.Process.Executable)
|
||||
}
|
||||
srcAddr := lookup(e.LocalAddr)
|
||||
dstAddr := lookup(e.RemoteAddr)
|
||||
if e.Process != nil && e.Process.Pid == session.PID && isSliverAddr(dstAddr, con) {
|
||||
con.Printf("%s%-5s %-23.23s %-23.23s %-12s %-16s%s\n",
|
||||
console.Green, e.Protocol, srcAddr, dstAddr, e.SkState, p, console.Normal)
|
||||
srcAddr := lookup(entry.LocalAddr)
|
||||
dstAddr := lookup(entry.RemoteAddr)
|
||||
if entry.Process != nil && entry.Process.Pid == implantPID {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(console.Green+"%s"+console.Normal, entry.Protocol),
|
||||
fmt.Sprintf(console.Green+"%s"+console.Normal, srcAddr),
|
||||
fmt.Sprintf(console.Green+"%s"+console.Normal, dstAddr),
|
||||
fmt.Sprintf(console.Green+"%s"+console.Normal, entry.SkState),
|
||||
fmt.Sprintf(console.Green+"%s"+console.Normal, pid),
|
||||
})
|
||||
} else {
|
||||
con.Printf("%-5s %-23.23s %-23.23s %-12s %-16s\n",
|
||||
e.Protocol, srcAddr, dstAddr, e.SkState, p)
|
||||
tw.AppendRow(table.Row{entry.Protocol, srcAddr, dstAddr, entry.SkState, pid})
|
||||
}
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func isSliverAddr(dstAddr string, con *console.SliverConsoleClient) bool {
|
||||
parts := strings.Split(dstAddr, ":")
|
||||
if len(parts) != 3 {
|
||||
return false
|
||||
func getActiveC2(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.ActiveC2
|
||||
}
|
||||
c2Addr := strings.Split(con.ActiveSession.GetInteractive().ActiveC2, "://")[1]
|
||||
return strings.Join(parts[:2], ":") == c2Addr
|
||||
if beacon != nil {
|
||||
return beacon.ActiveC2
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getPID(session *clientpb.Session, beacon *clientpb.Beacon) int32 {
|
||||
if session != nil {
|
||||
return session.PID
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.PID
|
||||
}
|
||||
return -1
|
||||
}
|
||||
@@ -19,17 +19,15 @@ package operators
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// OperatorsCmd - Display operators and current online status
|
||||
@@ -45,46 +43,24 @@ func OperatorsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
func displayOperators(operators []*clientpb.Operator, con *console.SliverConsoleClient) {
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
fmt.Fprintln(table, "Operator\tStatus\t")
|
||||
fmt.Fprintf(table, "%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Operator")),
|
||||
strings.Repeat("=", len("Status")),
|
||||
)
|
||||
|
||||
colorRow := []string{"", ""} // Two uncolored rows for the headers
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"Name",
|
||||
"Status",
|
||||
})
|
||||
for _, operator := range operators {
|
||||
// This is the CA, but I guess you could also name an operator
|
||||
// "multiplayer" and it'll never show up in the list
|
||||
if operator.Name == "multiplayer" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t\n", operator.Name, status(operator.Online))
|
||||
if operator.Online {
|
||||
colorRow = append(colorRow, console.Bold+console.Green)
|
||||
} else {
|
||||
colorRow = append(colorRow, "")
|
||||
}
|
||||
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
lines := strings.Split(outputBuf.String(), "\n")
|
||||
for lineNumber, line := range lines {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
con.Printf("%s%s%s\n", colorRow[lineNumber], line, console.Normal)
|
||||
tw.AppendRow(table.Row{
|
||||
console.Bold + operator.Name + console.Normal,
|
||||
status(operator.Online),
|
||||
})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
func status(isOnline bool) string {
|
||||
if isOnline {
|
||||
return "Online"
|
||||
return console.Bold + console.Green + "Online" + console.Normal
|
||||
}
|
||||
return "Offline"
|
||||
return console.Bold + console.Red + "Offline" + console.Normal
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// NamedPipeListenerCmd - Start a named pipe pivot listener on the remote system
|
||||
func NamedPipeListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func NamedPipeListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient
|
||||
|
||||
_, err := con.Rpc.NamedPipes(context.Background(), &sliverpb.NamedPipesReq{
|
||||
PipeName: pipeName,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -44,7 +44,7 @@ func PivotsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
printPivots(session, int64(timeout), con)
|
||||
} else {
|
||||
session := con.ActiveSession.Get()
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session != nil {
|
||||
printPivots(session, int64(timeout), con)
|
||||
} else {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// TCPListenerCmd - Start a TCP pivot listener on the remote system
|
||||
func TCPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -22,7 +22,7 @@ func TCPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
_, err := con.Rpc.TCPListener(context.Background(), &sliverpb.TCPPivotReq{
|
||||
Address: address,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
|
||||
// PortfwdAddCmd - Add a new tunneled port forward
|
||||
func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Prelude Operator
|
||||
=================
|
||||
|
||||
Connection code for [Prelude Operator](https://www.prelude.org/)
|
||||
@@ -0,0 +1,63 @@
|
||||
package operator
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
url := ctx.Args.String("connection-string")
|
||||
aesKey := ctx.Flags.String("aes-key")
|
||||
agentRange := ctx.Flags.String("range")
|
||||
skipExisting := ctx.Flags.Bool("skip-existing")
|
||||
|
||||
config := &prelude.OperatorConfig{
|
||||
Range: agentRange,
|
||||
OperatorURL: url,
|
||||
RPC: con.Rpc,
|
||||
AESKey: aesKey,
|
||||
}
|
||||
|
||||
sessionMapper := prelude.InitSessionMapper(config)
|
||||
|
||||
con.PrintInfof("Connected to Operator at %s\n", url)
|
||||
if !skipExisting {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not get session list: %s", err)
|
||||
return
|
||||
}
|
||||
if len(sessions.Sessions) > 0 {
|
||||
con.PrintInfof("Adding existing sessions ...\n")
|
||||
for _, session := range sessions.Sessions {
|
||||
err = sessionMapper.AddSession(session)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add session %s to session mapper: %s", session.Name, err)
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Done !\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package operator
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if prelude.SessionMapper != nil {
|
||||
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
|
||||
}
|
||||
@@ -1,13 +1,5 @@
|
||||
package privilege
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
@@ -26,40 +18,74 @@ import (
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// GetPrivsCmd - Get the current process privileges (Windows only)
|
||||
func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if session.OS != "windows" {
|
||||
con.PrintErrorf("Command not supported on this operating system.")
|
||||
targetOS := getOS(session, beacon)
|
||||
if targetOS != "windows" {
|
||||
con.PrintErrorf("Command only supported on Windows.\n")
|
||||
return
|
||||
}
|
||||
|
||||
privs, err := con.Rpc.GetPrivs(context.Background(), &sliverpb.GetPrivsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
pid := getPID(session, beacon)
|
||||
if privs.Response != nil && privs.Response.Async {
|
||||
con.AddBeaconCallback(privs.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, privs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintGetPrivs(privs, pid, con)
|
||||
})
|
||||
con.PrintAsyncResponse(privs.Response)
|
||||
} else {
|
||||
PrintGetPrivs(privs, pid, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintGetPrivs - Print the results of the get privs command
|
||||
func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverConsoleClient) {
|
||||
// Response is the Envelope (see RPC API), Err is part of it.
|
||||
if privs.Response != nil && privs.Response.Err != "" {
|
||||
con.PrintErrorf("NOTE: Information may be incomplete due to an error:\n")
|
||||
con.PrintErrorf("%s\n", privs.Response.Err)
|
||||
}
|
||||
|
||||
if privs.PrivInfo == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var processName string = "Current Process"
|
||||
if privs.ProcessName != "" {
|
||||
processName = privs.ProcessName
|
||||
}
|
||||
|
||||
// To make things look pretty, figure out the longest name and description
|
||||
// for column width
|
||||
var nameColumnWidth int = 0
|
||||
var descriptionColumnWidth int = 0
|
||||
var introWidth int = 34 + len(processName) + len(strconv.Itoa(int(pid)))
|
||||
|
||||
for _, entry := range privs.PrivInfo {
|
||||
if len(entry.Name) > nameColumnWidth {
|
||||
@@ -74,12 +100,13 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
nameColumnWidth += 1
|
||||
descriptionColumnWidth += 1
|
||||
|
||||
con.Println("Privilege Information for Current Process")
|
||||
con.Println("-----------------------------------------")
|
||||
con.Printf("Privilege Information for %s (PID: %d)\n", processName, pid)
|
||||
con.Println(strings.Repeat("-", introWidth))
|
||||
con.Printf("\nProcess Integrity Level: %s\n\n", privs.ProcessIntegrity)
|
||||
con.Printf("%-*s\t%-*s\t%s\n", nameColumnWidth, "Name", descriptionColumnWidth, "Description", "Attributes")
|
||||
con.Printf("%-*s\t%-*s\t%s\n", nameColumnWidth, "====", descriptionColumnWidth, "===========", "==========")
|
||||
for _, entry := range privs.PrivInfo {
|
||||
con.Printf("%-*s\t%-*s\t(", nameColumnWidth, entry.Name, descriptionColumnWidth, entry.Description)
|
||||
con.Printf("%-*s\t%-*s\t", nameColumnWidth, entry.Name, descriptionColumnWidth, entry.Description)
|
||||
if entry.Enabled {
|
||||
con.Printf("Enabled")
|
||||
} else {
|
||||
@@ -94,6 +121,26 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if entry.UsedForAccess {
|
||||
con.Printf(", Used for Access")
|
||||
}
|
||||
con.Printf(")\n")
|
||||
con.Printf("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.OS
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.OS
|
||||
}
|
||||
panic("no session or beacon")
|
||||
}
|
||||
|
||||
func getPID(session *clientpb.Session, beacon *clientpb.Beacon) int32 {
|
||||
if session != nil {
|
||||
return session.PID
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.PID
|
||||
}
|
||||
panic("no session or beacon")
|
||||
}
|
||||
@@ -23,34 +23,58 @@ import (
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// GetSystemCmd - Windows only, attempt to get SYSTEM on the remote system
|
||||
func GetSystemCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
targetOS := getOS(session, beacon)
|
||||
if targetOS != "windows" {
|
||||
con.PrintErrorf("Command only supported on Windows.\n")
|
||||
return
|
||||
}
|
||||
|
||||
process := ctx.Flags.String("process")
|
||||
config := con.GetActiveSessionConfig()
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Attempting to create a new sliver session as 'NT AUTHORITY\\SYSTEM'...", ctrl)
|
||||
|
||||
getsystemResp, err := con.Rpc.GetSystem(context.Background(), &clientpb.GetSystemReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
getSystem, err := con.Rpc.GetSystem(context.Background(), &clientpb.GetSystemReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Config: config,
|
||||
HostingProcess: process,
|
||||
})
|
||||
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if getsystemResp.GetResponse().GetErr() != "" {
|
||||
|
||||
if getSystem.Response != nil && getSystem.Response.Async {
|
||||
con.AddBeaconCallback(getSystem.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, getSystem)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintGetSystem(getSystem, con)
|
||||
})
|
||||
con.PrintAsyncResponse(getSystem.Response)
|
||||
} else {
|
||||
PrintGetSystem(getSystem, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintGetSystem - Print the results of get system
|
||||
func PrintGetSystem(getsystemResp *sliverpb.GetSystem, con *console.SliverConsoleClient) {
|
||||
if getsystemResp.Response != nil && getsystemResp.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", getsystemResp.GetResponse().GetErr())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -22,28 +22,48 @@ import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// ImpersonateCmd - Windows only, impersonate a user token
|
||||
func ImpersonateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
username := ctx.Args.String("username")
|
||||
impResp, err := con.Rpc.Impersonate(context.Background(), &sliverpb.ImpersonateReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
impersonate, err := con.Rpc.Impersonate(context.Background(), &sliverpb.ImpersonateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Username: username,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
if impResp.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", impResp.GetResponse().GetErr())
|
||||
|
||||
if impersonate.Response != nil && impersonate.Response.Async {
|
||||
con.AddBeaconCallback(impersonate.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, impersonate)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintImpersonate(impersonate, username, con)
|
||||
})
|
||||
con.PrintAsyncResponse(impersonate.Response)
|
||||
} else {
|
||||
PrintImpersonate(impersonate, username, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintImpersonate - Print the results of the attempted impersonation
|
||||
func PrintImpersonate(impersonate *sliverpb.Impersonate, username string, con *console.SliverConsoleClient) {
|
||||
if impersonate.Response != nil && impersonate.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", impersonate.Response.GetErr())
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully impersonated %s\n", username)
|
||||
|
||||
@@ -22,16 +22,19 @@ import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// MakeTokenCmd - Windows only, create a token using "valid" credentails
|
||||
func MakeTokenCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
username := ctx.Flags.String("username")
|
||||
password := ctx.Flags.String("password")
|
||||
domain := ctx.Flags.String("domain")
|
||||
@@ -45,22 +48,37 @@ func MakeTokenCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.SpinUntil("Creating new logon session ...", ctrl)
|
||||
|
||||
makeToken, err := con.Rpc.MakeToken(context.Background(), &sliverpb.MakeTokenReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Username: username,
|
||||
Domain: domain,
|
||||
Password: password,
|
||||
})
|
||||
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if makeToken.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", makeToken.GetResponse().GetErr())
|
||||
if makeToken.Response != nil && makeToken.Response.Async {
|
||||
con.AddBeaconCallback(makeToken.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, makeToken)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintMakeToken(makeToken, domain, username, con)
|
||||
})
|
||||
con.PrintAsyncResponse(makeToken.Response)
|
||||
} else {
|
||||
PrintMakeToken(makeToken, domain, username, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMakeToken - Print the results of attempting to make a token
|
||||
func PrintMakeToken(makeToken *sliverpb.MakeToken, domain string, username string, con *console.SliverConsoleClient) {
|
||||
if makeToken.Response != nil && makeToken.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", makeToken.Response.GetErr())
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
|
||||
@@ -22,22 +22,46 @@ import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// RevToSelfCmd - Drop any impersonated tokens
|
||||
func RevToSelfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
_, err := con.Rpc.RevToSelf(context.Background(), &sliverpb.RevToSelfReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
})
|
||||
|
||||
revert, err := con.Rpc.RevToSelf(context.Background(), &sliverpb.RevToSelfReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if revert.Response != nil && revert.Response.Async {
|
||||
con.AddBeaconCallback(revert.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, revert)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRev2Self(revert, con)
|
||||
})
|
||||
con.PrintAsyncResponse(revert.Response)
|
||||
} else {
|
||||
PrintRev2Self(revert, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRev2Self - Print the result of revert to self
|
||||
func PrintRev2Self(revert *sliverpb.RevToSelf, con *console.SliverConsoleClient) {
|
||||
if revert.Response != nil && revert.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", revert.Response.GetErr())
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Back to self...")
|
||||
|
||||
@@ -22,16 +22,19 @@ import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// RunAsCmd - Run a command as another user on the remote system
|
||||
func RunAsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
username := ctx.Flags.String("username")
|
||||
process := ctx.Flags.String("process")
|
||||
arguments := ctx.Flags.String("args")
|
||||
@@ -46,22 +49,49 @@ func RunAsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
runAsResp, err := con.Rpc.RunAs(context.Background(), &sliverpb.RunAsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
runAs, err := con.Rpc.RunAs(context.Background(), &sliverpb.RunAsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Username: username,
|
||||
ProcessName: process,
|
||||
Args: arguments,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if runAsResp.GetResponse().GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", runAsResp.GetResponse().GetErr())
|
||||
return
|
||||
name := getName(session, beacon)
|
||||
if runAs.Response != nil && runAs.Response.Async {
|
||||
con.AddBeaconCallback(runAs.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, runAs)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRunAs(runAs, process, arguments, name, con)
|
||||
})
|
||||
con.PrintAsyncResponse(runAs.Response)
|
||||
} else {
|
||||
PrintRunAs(runAs, process, arguments, name, con)
|
||||
}
|
||||
|
||||
con.PrintInfof("Successfully ran %s %s on %s\n", process, arguments, session.GetName())
|
||||
}
|
||||
|
||||
// PrintRunAs - Print the result of run as
|
||||
func PrintRunAs(runAs *sliverpb.RunAs, process string, args string, name string, con *console.SliverConsoleClient) {
|
||||
if runAs.Response != nil && runAs.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", runAs.Response.GetErr())
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully ran %s %s on %s\n", process, args, name)
|
||||
}
|
||||
|
||||
func getName(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.Name
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.Name
|
||||
}
|
||||
panic("no session or beacon")
|
||||
}
|
||||
@@ -22,22 +22,26 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// ProcdumpCmd - Dump the memory of a remote process
|
||||
func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pid := ctx.Flags.Int("pid")
|
||||
name := ctx.Flags.String("name")
|
||||
saveTo := ctx.Flags.String("save")
|
||||
|
||||
if pid == -1 && name != "" {
|
||||
pid = GetPIDByName(ctx, name, con)
|
||||
@@ -55,7 +59,7 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Dumping remote process memory ...", ctrl)
|
||||
dump, err := con.Rpc.ProcessDump(context.Background(), &sliverpb.ProcessDumpReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: int32(pid),
|
||||
Timeout: int32(ctx.Flags.Int("timeout") - 1),
|
||||
})
|
||||
@@ -66,13 +70,52 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
hostname := session.Hostname
|
||||
tmpFileName := path.Base(fmt.Sprintf("procdump_%s_%d_*", hostname, pid))
|
||||
tmpFile, err := ioutil.TempFile("", tmpFileName)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error creating temporary file: %v\n", err)
|
||||
return
|
||||
hostname := getHostname(session, beacon)
|
||||
if dump.Response != nil && dump.Response.Async {
|
||||
con.AddBeaconCallback(dump.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, dump)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintProcessDump(dump, saveTo, hostname, pid, con)
|
||||
})
|
||||
con.PrintAsyncResponse(dump.Response)
|
||||
} else {
|
||||
PrintProcessDump(dump, saveTo, hostname, pid, con)
|
||||
}
|
||||
tmpFile.Write(dump.GetData())
|
||||
con.PrintInfof("Process dump stored in: %s\n", tmpFile.Name())
|
||||
|
||||
}
|
||||
|
||||
// PrintProcessDump - Handle the results of a process dump
|
||||
func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string, pid int, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
var saveToFile *os.File
|
||||
if saveTo == "" {
|
||||
tmpFileName := filepath.Base(fmt.Sprintf("procdump_%s_%d_*", hostname, pid))
|
||||
saveToFile, err = ioutil.TempFile("", tmpFileName)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error creating temporary file: %s\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error creating file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer saveToFile.Close()
|
||||
saveToFile.Write(dump.GetData())
|
||||
con.PrintInfof("Process dump stored in: %s\n", saveToFile.Name())
|
||||
}
|
||||
|
||||
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.Hostname
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.Hostname
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -19,24 +19,23 @@ package processes
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
// "time"
|
||||
|
||||
"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/bishopfox/sliver/protobuf/sliverpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
// Stylizes known processes in the `ps` command
|
||||
knownProcs = map[string]string{
|
||||
knownSecurityTools = map[string]string{
|
||||
"ccSvcHst.exe": console.Red, // SEP
|
||||
"cb.exe": console.Red, // Carbon Black
|
||||
"MsMpEng.exe": console.Red, // Windows Defender
|
||||
@@ -46,107 +45,111 @@ var (
|
||||
|
||||
// PsCmd - List processes on the remote system
|
||||
func PsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pidFilter := ctx.Flags.Int("pid")
|
||||
exeFilter := ctx.Flags.String("exe")
|
||||
ownerFilter := ctx.Flags.String("owner")
|
||||
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
os := getOS(session, beacon)
|
||||
if ps.Response != nil && ps.Response.Async {
|
||||
con.AddBeaconCallback(ps.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, ps)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintPS(os, ps, false, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(ps.Response)
|
||||
} else {
|
||||
PrintPS(os, ps, true, ctx, con)
|
||||
}
|
||||
}
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', tabwriter.DiscardEmptyColumns)
|
||||
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.OS
|
||||
} else if beacon != nil {
|
||||
return beacon.OS
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
switch session.GetOS() {
|
||||
// PrintPS - Prints the process list
|
||||
func PrintPS(os string, ps *sliverpb.Ps, interactive bool, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
pidFilter := ctx.Flags.Int("pid")
|
||||
exeFilter := ctx.Flags.String("exe")
|
||||
ownerFilter := ctx.Flags.String("owner")
|
||||
overflow := ctx.Flags.Bool("overflow")
|
||||
skipPages := ctx.Flags.Int("skip-pages")
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
|
||||
switch os {
|
||||
case "windows":
|
||||
fmt.Fprintf(table, "pid\tppid\towner\texecutable\tsession\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("pid")),
|
||||
strings.Repeat("=", len("ppid")),
|
||||
strings.Repeat("=", len("owner")),
|
||||
strings.Repeat("=", len("executable")),
|
||||
strings.Repeat("=", len("session")),
|
||||
)
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable", "session"})
|
||||
case "darwin":
|
||||
fallthrough
|
||||
case "linux":
|
||||
fmt.Fprintf(table, "pid\tppid\towner\texecutable\t\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("pid")),
|
||||
strings.Repeat("=", len("ppid")),
|
||||
strings.Repeat("=", len("owner")),
|
||||
strings.Repeat("=", len("executable")),
|
||||
)
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
|
||||
default:
|
||||
fmt.Fprintf(table, "pid\tppid\towner\texecutable\t\n")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("pid")),
|
||||
strings.Repeat("=", len("ppid")),
|
||||
strings.Repeat("=", len("owner")),
|
||||
strings.Repeat("=", len("executable")),
|
||||
)
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
|
||||
}
|
||||
|
||||
cmdLine := ctx.Flags.Bool("print-cmdline")
|
||||
lineColors := []string{}
|
||||
for _, proc := range ps.Processes {
|
||||
var lineColor = ""
|
||||
if pidFilter != -1 && proc.Pid == int32(pidFilter) {
|
||||
lineColor = printProcInfo(table, proc, cmdLine, con)
|
||||
}
|
||||
if exeFilter != "" && strings.HasPrefix(proc.Executable, exeFilter) {
|
||||
lineColor = printProcInfo(table, proc, cmdLine, con)
|
||||
}
|
||||
if ownerFilter != "" && strings.HasPrefix(proc.Owner, ownerFilter) {
|
||||
lineColor = printProcInfo(table, proc, cmdLine, con)
|
||||
}
|
||||
if pidFilter == -1 && exeFilter == "" && ownerFilter == "" {
|
||||
lineColor = printProcInfo(table, proc, cmdLine, con)
|
||||
}
|
||||
|
||||
// Should be set to normal/green if we rendered the line
|
||||
if lineColor != "" {
|
||||
lineColors = append(lineColors, lineColor)
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
for index, line := range strings.Split(outputBuf.String(), "\n") {
|
||||
if len(line) == 0 {
|
||||
if pidFilter != -1 && proc.Pid != int32(pidFilter) {
|
||||
continue
|
||||
}
|
||||
// We need to account for the two rows of column headers
|
||||
if 0 < len(line) && 2 <= index {
|
||||
lineColor := lineColors[index-2]
|
||||
con.Printf("%s%s%s\n", lineColor, line, console.Normal)
|
||||
} else {
|
||||
con.Printf("%s\n", line)
|
||||
if exeFilter != "" && !strings.Contains(strings.ToLower(proc.Executable), strings.ToLower(exeFilter)) {
|
||||
continue
|
||||
}
|
||||
if ownerFilter != "" && !strings.Contains(strings.ToLower(proc.Owner), strings.ToLower(ownerFilter)) {
|
||||
continue
|
||||
}
|
||||
procRow(tw, proc, cmdLine, con)
|
||||
}
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "pid", Mode: table.Asc},
|
||||
{Name: "ppid", Mode: table.Asc},
|
||||
})
|
||||
|
||||
settings.PaginateTable(tw, skipPages, overflow, interactive, con)
|
||||
}
|
||||
|
||||
// printProcInfo - Stylizes the process information
|
||||
func printProcInfo(table *tabwriter.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverConsoleClient) string {
|
||||
// procRow - Stylizes the process information
|
||||
func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
|
||||
color := console.Normal
|
||||
if modifyColor, ok := knownProcs[proc.Executable]; ok {
|
||||
if modifyColor, ok := knownSecurityTools[proc.Executable]; ok {
|
||||
color = modifyColor
|
||||
}
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session != nil && proc.Pid == session.PID {
|
||||
color = console.Green
|
||||
}
|
||||
if beacon != nil && proc.Pid == beacon.PID {
|
||||
color = console.Green
|
||||
}
|
||||
|
||||
var row table.Row
|
||||
switch session.GetOS() {
|
||||
case "windows":
|
||||
fmt.Fprintf(table, "%d\t%d\t%s\t%s\t%d\t\n", proc.Pid, proc.Ppid, proc.Owner, proc.Executable, proc.SessionID)
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
|
||||
}
|
||||
case "darwin":
|
||||
fallthrough
|
||||
case "linux":
|
||||
@@ -159,18 +162,28 @@ func printProcInfo(table *tabwriter.Writer, proc *commonpb.Process, cmdLine bool
|
||||
} else {
|
||||
args = proc.Executable
|
||||
}
|
||||
fmt.Fprintf(table, "%d\t%d\t%s\t%s\t\n", proc.Pid, proc.Ppid, proc.Owner, args)
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, args),
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(table, "%d\t%d\t%s\t%s\t\n", proc.Pid, proc.Ppid, proc.Owner, proc.Executable)
|
||||
row = table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
}
|
||||
}
|
||||
}
|
||||
return color
|
||||
tw.AppendRow(row)
|
||||
}
|
||||
|
||||
// GetPIDByName - Get a PID by name from the active session
|
||||
func GetPIDByName(ctx *grumble.Context, name string, con *console.SliverConsoleClient) int {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return -1
|
||||
|
||||
@@ -22,27 +22,46 @@ import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// TerminateCmd - Terminate a process on the remote system
|
||||
func TerminateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pid := ctx.Args.Uint("pid")
|
||||
terminated, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: int32(pid),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
if terminated.Response != nil && terminated.Response.Async {
|
||||
con.AddBeaconCallback(terminated.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, terminated)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintTerminate(terminated, con)
|
||||
})
|
||||
con.PrintAsyncResponse(terminated.Response)
|
||||
} else {
|
||||
PrintTerminate(terminated, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintTerminate - Print the results of the terminate command
|
||||
func PrintTerminate(terminated *sliverpb.Terminate, con *console.SliverConsoleClient) {
|
||||
if terminated.Response != nil && terminated.Response.GetErr() != "" {
|
||||
con.PrintErrorf("%s\n", terminated.Response.GetErr())
|
||||
} else {
|
||||
con.PrintInfof("Process %d has been terminated\n", terminated.Pid)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
// ReactionSaveCmd - Manage reactions to events
|
||||
func ReactionReloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if _, err := os.Stat(GetReactionFilePath()); os.IsNotExist(err) {
|
||||
con.PrintWarnf("Missing reaction file %s\n", GetReactionFilePath())
|
||||
con.PrintErrorf("Missing reaction file %s\n", GetReactionFilePath())
|
||||
return
|
||||
}
|
||||
confirm := false
|
||||
|
||||
@@ -47,7 +47,7 @@ func ReactionUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Println()
|
||||
con.PrintInfof("Successfully removed reaction with id %d", reactionID)
|
||||
} else {
|
||||
con.PrintWarnf("No reaction found with id %d", reactionID)
|
||||
con.PrintErrorf("No reaction found with id %d", reactionID)
|
||||
}
|
||||
con.Println()
|
||||
}
|
||||
|
||||
@@ -23,14 +23,21 @@ import (
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// RegCreateKeyCmd - Create a new Windows registry key
|
||||
func RegCreateKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.Get()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
targetOS := getOS(session, beacon)
|
||||
if targetOS != "windows" {
|
||||
con.PrintErrorf("Registry operations can only target Windows\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,22 +67,49 @@ func RegCreateKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
finalPath := regPath[:pathBaseIdx]
|
||||
key := regPath[pathBaseIdx+1:]
|
||||
createKeyResp, err := con.Rpc.RegistryCreateKey(context.Background(), &sliverpb.RegistryCreateKeyReq{
|
||||
|
||||
createKey, err := con.Rpc.RegistryCreateKey(context.Background(), &sliverpb.RegistryCreateKeyReq{
|
||||
Hive: hive,
|
||||
Path: finalPath,
|
||||
Key: key,
|
||||
Hostname: hostname,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if createKeyResp.Response != nil && createKeyResp.Response.Err != "" {
|
||||
con.PrintErrorf("%s", createKeyResp.Response.Err)
|
||||
if createKey.Response != nil && createKey.Response.Async {
|
||||
con.AddBeaconCallback(createKey.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, createKey)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCreateKey(createKey, finalPath, key, con)
|
||||
})
|
||||
con.PrintAsyncResponse(createKey.Response)
|
||||
} else {
|
||||
PrintCreateKey(createKey, finalPath, key, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCreateKey - Print the results of the crate key command
|
||||
func PrintCreateKey(createKey *sliverpb.RegistryCreateKey, regPath string, key string, con *console.SliverConsoleClient) {
|
||||
if createKey.Response != nil && createKey.Response.Err != "" {
|
||||
con.PrintErrorf("%s", createKey.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Key created at %s\\%s", regPath, key)
|
||||
}
|
||||
|
||||
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.OS
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.OS
|
||||
}
|
||||
panic("no session or beacon")
|
||||
}
|
||||
@@ -1,50 +1,93 @@
|
||||
package registry
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
// RegListSubKeysCmd - List sub registry keys
|
||||
func RegListSubKeysCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
targetOS := getOS(session, beacon)
|
||||
if targetOS != "windows" {
|
||||
con.PrintErrorf("Registry operations can only target Windows\n")
|
||||
return
|
||||
}
|
||||
|
||||
path := ctx.Args.String("registry-path")
|
||||
regPath := ctx.Args.String("registry-path")
|
||||
hive := ctx.Flags.String("hive")
|
||||
hostname := ctx.Flags.String("hostname")
|
||||
|
||||
regList, err := con.Rpc.RegistryListSubKeys(context.Background(), &sliverpb.RegistrySubKeyListReq{
|
||||
Hive: hive,
|
||||
Hostname: hostname,
|
||||
Path: path,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Path: regPath,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err.Error())
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if regList.Response != nil && regList.Response.Async {
|
||||
con.AddBeaconCallback(regList.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, regList)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintListSubKeys(regList, hive, regPath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(regList.Response)
|
||||
} else {
|
||||
PrintListSubKeys(regList, hive, regPath, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintListSubKeys - Print the list sub keys command result
|
||||
func PrintListSubKeys(regList *sliverpb.RegistrySubKeyList, hive string, regPath string, con *console.SliverConsoleClient) {
|
||||
if regList.Response != nil && regList.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", regList.Response.Err)
|
||||
con.PrintErrorf("%s\n", regList.Response.Err)
|
||||
return
|
||||
}
|
||||
if len(regList.Subkeys) > 0 {
|
||||
con.PrintInfof("Sub keys under %s:\\%s:\n", hive, path)
|
||||
if 0 < len(regList.Subkeys) {
|
||||
con.PrintInfof("Sub keys under %s:\\%s:\n", hive, regPath)
|
||||
}
|
||||
for _, subKey := range regList.Subkeys {
|
||||
con.Println(subKey)
|
||||
}
|
||||
}
|
||||
|
||||
// RegListValuesCmd - List registry values
|
||||
func RegListValuesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -56,19 +99,35 @@ func RegListValuesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Hive: hive,
|
||||
Hostname: hostname,
|
||||
Path: regPath,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err.Error())
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if regList.Response != nil && regList.Response.Async {
|
||||
con.AddBeaconCallback(regList.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, regList)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintListValues(regList, hive, regPath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(regList.Response)
|
||||
} else {
|
||||
PrintListValues(regList, hive, regPath, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintListValues - Print the registry list values
|
||||
func PrintListValues(regList *sliverpb.RegistryValuesList, hive string, regPath string, con *console.SliverConsoleClient) {
|
||||
if regList.Response != nil && regList.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", regList.Response.Err)
|
||||
con.PrintErrorf("%s\n", regList.Response.Err)
|
||||
return
|
||||
}
|
||||
if len(regList.ValueNames) > 0 {
|
||||
if 0 < len(regList.ValueNames) {
|
||||
con.PrintInfof("Values under %s:\\%s:\n", hive, regPath)
|
||||
}
|
||||
for _, val := range regList.ValueNames {
|
||||
|
||||
@@ -24,8 +24,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var validHives = []string{
|
||||
@@ -76,12 +78,13 @@ func RegReadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
finalPath string
|
||||
key string
|
||||
)
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
if session.OS != "windows" {
|
||||
con.PrintErrorf("Command not supported on this operating system.")
|
||||
targetOS := getOS(session, beacon)
|
||||
if targetOS != "windows" {
|
||||
con.PrintErrorf("Registry operations can only target Windows\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -112,19 +115,36 @@ func RegReadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
finalPath = regPath[:pathBaseIdx]
|
||||
key = regPath[pathBaseIdx+1:]
|
||||
|
||||
regRead, err := con.Rpc.RegistryRead(context.Background(), &sliverpb.RegistryReadReq{
|
||||
Hive: hive,
|
||||
Path: finalPath,
|
||||
Key: key,
|
||||
Hostname: hostname,
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if regRead.Response != nil && regRead.Response.Async {
|
||||
con.AddBeaconCallback(regRead.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, regRead)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRegRead(regRead, con)
|
||||
})
|
||||
con.PrintAsyncResponse(regRead.Response)
|
||||
} else {
|
||||
PrintRegRead(regRead, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRegRead - Print the results of the registry read command
|
||||
func PrintRegRead(regRead *sliverpb.RegistryRead, con *console.SliverConsoleClient) {
|
||||
if regRead.Response != nil && regRead.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", regRead.Response.Err)
|
||||
return
|
||||
|
||||
@@ -26,18 +26,21 @@ import (
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// RegWriteCmd - Write to a Windows registry key: registry write --hive HKCU --type dword "software\google\chrome\blbeacon\hello" 32
|
||||
func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
if session.OS != "windows" {
|
||||
con.PrintErrorf("Error: command not supported on this operating system.")
|
||||
targetOS := getOS(session, beacon)
|
||||
if targetOS != "windows" {
|
||||
con.PrintErrorf("Registry operations can only target Windows\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -123,7 +126,7 @@ func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
regWrite, err := con.Rpc.RegistryWrite(context.Background(), &sliverpb.RegistryWriteReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Hostname: hostname,
|
||||
Hive: hive,
|
||||
Path: finalPath,
|
||||
@@ -134,11 +137,28 @@ func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
QWordValue: qwordValue,
|
||||
ByteValue: binaryValue,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if regWrite.Response != nil && regWrite.Response.Async {
|
||||
con.AddBeaconCallback(regWrite.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, regWrite)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRegWrite(regWrite, con)
|
||||
})
|
||||
con.PrintAsyncResponse(regWrite.Response)
|
||||
} else {
|
||||
PrintRegWrite(regWrite, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintRegWrite - Print the registry write operation
|
||||
func PrintRegWrite(regWrite *sliverpb.RegistryWrite, con *console.SliverConsoleClient) {
|
||||
if regWrite.Response != nil && regWrite.Response.Err != "" {
|
||||
con.PrintErrorf("%s", regWrite.Response.Err)
|
||||
return
|
||||
|
||||
@@ -22,52 +22,89 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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/bishopfox/sliver/util"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ScreenshotCmd - Take a screenshot of the remote system
|
||||
func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if session.OS != "windows" && session.OS != "linux" {
|
||||
con.PrintErrorf("Not implemented for %s\n", session.OS)
|
||||
targetOS := getOS(session, beacon)
|
||||
if targetOS != "windows" && targetOS != "linux" {
|
||||
con.PrintWarnf("Target platform may not support screenshots!\n")
|
||||
return
|
||||
}
|
||||
|
||||
screenshot, err := con.Rpc.Screenshot(context.Background(), &sliverpb.ScreenshotReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
timestamp := time.Now().Format("20060102150405")
|
||||
tmpFileName := path.Base(fmt.Sprintf("screenshot_%s_%d_%s_*.png", session.Name, session.ID, timestamp))
|
||||
tmpFile, err := ioutil.TempFile("", tmpFileName)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
hostname := getHostname(session, beacon)
|
||||
if screenshot.Response != nil && screenshot.Response.Async {
|
||||
con.AddBeaconCallback(screenshot.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, screenshot)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintScreenshot(screenshot, hostname, ctx, con)
|
||||
})
|
||||
con.PrintAsyncResponse(screenshot.Response)
|
||||
} else {
|
||||
PrintScreenshot(screenshot, hostname, ctx, con)
|
||||
}
|
||||
err = ioutil.WriteFile(tmpFile.Name(), screenshot.Data, 0600)
|
||||
}
|
||||
|
||||
// PrintScreenshot - Handle the screenshot command response
|
||||
func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
timestamp := time.Now().Format("20060102150405")
|
||||
|
||||
saveTo := ctx.Flags.String("save")
|
||||
var saveToFile *os.File
|
||||
var err error
|
||||
if saveTo == "" {
|
||||
tmpFileName := filepath.Base(fmt.Sprintf("screenshot_%s_%s_*.png", filepath.Base(hostname), timestamp))
|
||||
saveToFile, err = ioutil.TempFile("", tmpFileName)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error creating file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer saveToFile.Close()
|
||||
var n int
|
||||
n, err = saveToFile.Write(screenshot.Data)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error writting file: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf(console.Bold+"Screenshot written to %s\n", tmpFile.Name())
|
||||
|
||||
con.PrintInfof("Screenshot written to %s (%s)\n", saveToFile.Name(), util.ByteCountBinary(int64(n)))
|
||||
if ctx.Flags.Bool("loot") && 0 < len(screenshot.Data) {
|
||||
err = loot.AddLootFile(con.Rpc, fmt.Sprintf("[screenshot] %s", timestamp), tmpFileName, screenshot.Data, false)
|
||||
err = loot.AddLootFile(con.Rpc, fmt.Sprintf("[screenshot] %s", timestamp), saveToFile.Name(), screenshot.Data, false)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save output as loot: %s\n", err)
|
||||
} else {
|
||||
@@ -75,3 +112,23 @@ func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.OS
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.OS
|
||||
}
|
||||
panic("no session or beacon")
|
||||
}
|
||||
|
||||
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
if session != nil {
|
||||
return session.Hostname
|
||||
}
|
||||
if beacon != nil {
|
||||
return beacon.Hostname
|
||||
}
|
||||
panic("no session or beacon")
|
||||
}
|
||||
@@ -7,6 +7,6 @@ import (
|
||||
|
||||
// BackgroundCmd - Background the active session
|
||||
func BackgroundCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.ActiveSession.Background()
|
||||
con.ActiveTarget.Background()
|
||||
con.PrintInfof("Background ...\n")
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package sessions
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// InteractiveCmd - Beacon only command to open an interactive session
|
||||
func InteractiveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
beacon := con.ActiveTarget.GetBeaconInteractive()
|
||||
if beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
delay, err := time.ParseDuration(ctx.Flags.String("delay"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
|
||||
mtlsC2 := generate.ParseMTLSc2(ctx.Flags.String("mtls"))
|
||||
c2s = append(c2s, mtlsC2...)
|
||||
|
||||
wgC2 := generate.ParseWGc2(ctx.Flags.String("wg"))
|
||||
c2s = append(c2s, wgC2...)
|
||||
|
||||
httpC2 := generate.ParseHTTPc2(ctx.Flags.String("http"))
|
||||
c2s = append(c2s, httpC2...)
|
||||
|
||||
dnsC2 := generate.ParseDNSc2(ctx.Flags.String("dns"))
|
||||
c2s = append(c2s, dnsC2...)
|
||||
|
||||
namedPipeC2 := generate.ParseNamedPipec2(ctx.Flags.String("named-pipe"))
|
||||
c2s = append(c2s, namedPipeC2...)
|
||||
|
||||
tcpPivotC2 := generate.ParseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
|
||||
c2s = append(c2s, tcpPivotC2...)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
openSession := &sliverpb.OpenSession{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
C2S: []string{},
|
||||
Delay: int64(delay),
|
||||
}
|
||||
for _, c2 := range c2s {
|
||||
openSession.C2S = append(openSession.C2S, c2.URL)
|
||||
}
|
||||
|
||||
openSession, err = con.Rpc.OpenSession(context.Background(), openSession)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
if openSession.Response != nil && openSession.Response.Async {
|
||||
con.PrintAsyncResponse(openSession.Response)
|
||||
} else {
|
||||
con.PrintErrorf("rpc response missing!\n")
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package sessions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"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"
|
||||
)
|
||||
|
||||
// KillCmd - Kill the active session (not to be confused with TerminateCmd)
|
||||
func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
con.PrintWarnf("No active session\n")
|
||||
return
|
||||
}
|
||||
// Confirm with the user, just in case they confused kill with terminate
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Kill the active session?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
|
||||
err := killSession(session, ctx.Flags.Bool("force"), con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
|
||||
con.ActiveSession.Background()
|
||||
}
|
||||
|
||||
func killSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
|
||||
if session == nil {
|
||||
return errors.New("session does not exist")
|
||||
}
|
||||
_, err := con.Rpc.KillSession(context.Background(), &sliverpb.KillSessionReq{
|
||||
Request: &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
},
|
||||
Force: force,
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -1,8 +1,27 @@
|
||||
package sessions
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/kill"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
@@ -22,7 +41,7 @@ func SessionsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
for _, session := range sessions.GetSessions() {
|
||||
if session.IsDead {
|
||||
con.Printf("Pruning session #%d ...", session.ID)
|
||||
err = killSession(session, true, con)
|
||||
err = kill.KillSession(session, true, con)
|
||||
if err != nil {
|
||||
con.Printf("failed!\n")
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,6 +21,7 @@ package sessions
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -30,7 +31,7 @@ import (
|
||||
|
||||
// SessionsReconfigCmd - Reconfigure metadata about a sessions
|
||||
func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -51,11 +52,12 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
// Option to change the reconnect interval
|
||||
poll := ctx.Flags.Int("poll")
|
||||
|
||||
oldSession := con.ActiveTarget.GetSession()
|
||||
session, err := con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
|
||||
SessionID: con.ActiveSession.Session.ID,
|
||||
SessionID: oldSession.ID,
|
||||
Name: name,
|
||||
ReconnectInterval: int32(reconnect),
|
||||
PollInterval: int32(poll),
|
||||
ReconnectInterval: int64(reconnect) * int64(time.Second),
|
||||
PollInterval: int64(poll) * int64(time.Second),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -63,5 +65,5 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
con.ActiveSession.Set(session)
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
}
|
||||
@@ -19,25 +19,24 @@ package sessions
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// SessionsCmd - Display/interact with sessions
|
||||
func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
interact := ctx.Flags.String("interact")
|
||||
kill := ctx.Flags.String("kill")
|
||||
killFlag := ctx.Flags.String("kill")
|
||||
killAll := ctx.Flags.Bool("kill-all")
|
||||
clean := ctx.Flags.Bool("clean")
|
||||
|
||||
@@ -48,9 +47,9 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if killAll {
|
||||
con.ActiveSession.Background()
|
||||
con.ActiveTarget.Background()
|
||||
for _, session := range sessions.Sessions {
|
||||
err := killSession(session, true, con)
|
||||
err := kill.KillSession(session, true, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
@@ -61,10 +60,10 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if clean {
|
||||
con.ActiveSession.Background()
|
||||
con.ActiveTarget.Background()
|
||||
for _, session := range sessions.Sessions {
|
||||
if session.IsDead {
|
||||
err := killSession(session, true, con)
|
||||
err := kill.KillSession(session, true, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
@@ -74,13 +73,13 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if kill != "" {
|
||||
session := con.GetSession(kill)
|
||||
activeSession := con.ActiveSession.Get()
|
||||
if killFlag != "" {
|
||||
session := con.GetSession(killFlag)
|
||||
activeSession := con.ActiveTarget.GetSession()
|
||||
if activeSession != nil && session.ID == activeSession.ID {
|
||||
con.ActiveSession.Background()
|
||||
con.ActiveTarget.Background()
|
||||
}
|
||||
err := killSession(session, true, con)
|
||||
err := kill.KillSession(session, true, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
@@ -90,7 +89,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if interact != "" {
|
||||
session := con.GetSession(interact)
|
||||
if session != nil {
|
||||
con.ActiveSession.Set(session)
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
|
||||
} else {
|
||||
con.PrintErrorf("Invalid session name or session number: %s\n", interact)
|
||||
@@ -101,55 +100,34 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
sessionsMap[session.ID] = session
|
||||
}
|
||||
if 0 < len(sessionsMap) {
|
||||
printSessions(sessionsMap, con)
|
||||
PrintSessions(sessionsMap, con)
|
||||
} else {
|
||||
con.PrintInfof("No sessions 🙁\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
So this method is a little more complex than you'd maybe think,
|
||||
this is because Go's tabwriter aligns columns by counting bytes
|
||||
and since we want to modify the color of the active sliver row
|
||||
the number of bytes per row won't line up. So we render the table
|
||||
into a buffer and note which row the active sliver is in. Then we
|
||||
write each line to the term and insert the ANSI codes just before
|
||||
we display the row.
|
||||
*/
|
||||
func printSessions(sessions map[uint32]*clientpb.Session, con *console.SliverConsoleClient) {
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
// PrintSessions - Print the current sessions
|
||||
func PrintSessions(sessions map[uint32]*clientpb.Session, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Last Check-in",
|
||||
"Health",
|
||||
})
|
||||
|
||||
// Column Headers
|
||||
fmt.Fprintln(table, "ID\tName\tTransport\tRemote Address\tHostname\tUsername\tOperating System\tLast Check-in\tHealth\t")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("ID")),
|
||||
strings.Repeat("=", len("Name")),
|
||||
strings.Repeat("=", len("Transport")),
|
||||
strings.Repeat("=", len("Remote Address")),
|
||||
strings.Repeat("=", len("Hostname")),
|
||||
strings.Repeat("=", len("Username")),
|
||||
strings.Repeat("=", len("Operating System")),
|
||||
strings.Repeat("=", len("Last Check-in")),
|
||||
strings.Repeat("=", len("Health")),
|
||||
)
|
||||
// strings.Repeat("=", len("Burned")))
|
||||
|
||||
// Sort the keys because maps have a randomized order
|
||||
var keys []int
|
||||
for _, session := range sessions {
|
||||
keys = append(keys, int(session.ID))
|
||||
}
|
||||
sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's
|
||||
|
||||
activeIndex := -1
|
||||
for index, key := range keys {
|
||||
session := sessions[uint32(key)]
|
||||
if con.ActiveSession.Get() != nil && con.ActiveSession.Get().ID == session.ID {
|
||||
activeIndex = index + 2 // Two lines for the headers
|
||||
color := console.Normal
|
||||
if con.ActiveTarget.GetSession() != nil && con.ActiveTarget.GetSession().ID == session.ID {
|
||||
color = console.Green
|
||||
}
|
||||
|
||||
var SessionHealth string
|
||||
if session.IsDead {
|
||||
SessionHealth = console.Bold + console.Red + "[DEAD]" + console.Normal
|
||||
@@ -160,35 +138,21 @@ func printSessions(sessions map[uint32]*clientpb.Session, con *console.SliverCon
|
||||
if session.Burned {
|
||||
burned = "🔥"
|
||||
}
|
||||
|
||||
fmt.Fprintf(table, "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
session.ID,
|
||||
session.Name,
|
||||
session.Transport,
|
||||
session.RemoteAddress,
|
||||
session.Hostname,
|
||||
session.Username,
|
||||
fmt.Sprintf("%s/%s", session.OS, session.Arch),
|
||||
session.LastCheckin,
|
||||
SessionHealth,
|
||||
burned,
|
||||
)
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%d"+console.Normal, session.ID),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, session.Name),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, session.Transport),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, session.RemoteAddress),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, session.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, session.Username),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, time.Unix(session.LastCheckin, 0).Format(time.RFC1123)),
|
||||
burned + SessionHealth,
|
||||
})
|
||||
}
|
||||
table.Flush()
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "ID", Mode: table.Asc},
|
||||
})
|
||||
|
||||
if activeIndex != -1 {
|
||||
lines := strings.Split(outputBuf.String(), "\n")
|
||||
for lineNumber, line := range lines {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if lineNumber == activeIndex {
|
||||
con.Printf("%s%s%s\n", console.Green, line, console.Normal)
|
||||
} else {
|
||||
con.Printf("%s\n", line)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
con.Printf(outputBuf.String())
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package sessions
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// UseCmd - Change the active session
|
||||
func UseCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
sessionArg := ctx.Args.String("session")
|
||||
if sessionArg != "" {
|
||||
session := con.GetSession(sessionArg)
|
||||
if session == nil {
|
||||
con.PrintErrorf("Invalid session name or session number '%s'\n", ctx.Args.String("session"))
|
||||
} else {
|
||||
activate(session, con)
|
||||
}
|
||||
} else {
|
||||
session, err := SelectSession(false, con)
|
||||
if session != nil {
|
||||
activate(session, con)
|
||||
} else if err != nil {
|
||||
switch err {
|
||||
case ErrNoSessions:
|
||||
con.PrintErrorf("No sessions available\n")
|
||||
case ErrNoSelection:
|
||||
con.PrintErrorf("No session selected\n")
|
||||
default:
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func activate(session *clientpb.Session, con *console.SliverConsoleClient) {
|
||||
con.ActiveSession.Set(session)
|
||||
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Style
|
||||
======
|
||||
|
||||
Defines terminal interface styles.
|
||||
@@ -0,0 +1,39 @@
|
||||
package settings
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// SettingsBeaconsAutoResultCmd - The client settings command
|
||||
func SettingsBeaconsAutoResultCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if con.Settings == nil {
|
||||
con.Settings, err = assets.LoadSettings()
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
con.Settings.BeaconAutoResults = !con.Settings.BeaconAutoResults
|
||||
con.PrintInfof("Beacon Auto Result = %v\n", con.Settings.BeaconAutoResults)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package settings
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// SettingsAutoAdultCmd - The client settings command
|
||||
func SettingsAutoAdultCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if con.Settings == nil {
|
||||
con.Settings, err = assets.LoadSettings()
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
con.Settings.AutoAdult = !con.Settings.AutoAdult
|
||||
con.PrintInfof("Auto Adult = %v\n", con.Settings.AutoAdult)
|
||||
}
|
||||
|
||||
// IsUserAnAdult - This should be called for any dangerous (OPSEC-wise) functions
|
||||
func IsUserAnAdult(con *console.SliverConsoleClient) bool {
|
||||
if GetAutoAdult(con) {
|
||||
return true
|
||||
}
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "This action is bad OPSEC, are you an adult?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
return confirm
|
||||
}
|
||||
|
||||
// GetAutoAdult - Get the current auto adult setting
|
||||
func GetAutoAdult(con *console.SliverConsoleClient) bool {
|
||||
if con.Settings == nil {
|
||||
con.Settings, _ = assets.LoadSettings()
|
||||
}
|
||||
if con.Settings != nil {
|
||||
return con.Settings.AutoAdult
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package settings
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// SettingsCmd - The client settings command
|
||||
func SettingsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if con.Settings == nil {
|
||||
con.Settings, err = assets.LoadSettings()
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{"Name", "Value", "Description"})
|
||||
tw.AppendRow(table.Row{"Tables", con.Settings.TableStyle, "Set the stylization of tables"})
|
||||
tw.AppendRow(table.Row{"Auto Adult", con.Settings.AutoAdult, "Automatically accept OPSEC warnings"})
|
||||
tw.AppendRow(table.Row{"Beacon Auto Results", con.Settings.BeaconAutoResults, "Automatically display beacon results when tasks complete"})
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
// SettingsTablesCmd - The client settings command
|
||||
func SettingsTablesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if con.Settings == nil {
|
||||
con.Settings, err = assets.LoadSettings()
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
options := []string{}
|
||||
for option := range tableStyles {
|
||||
options = append(options, option)
|
||||
}
|
||||
style := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Choose a style:",
|
||||
Options: options,
|
||||
}
|
||||
err = survey.AskOne(prompt, &style)
|
||||
if err != nil {
|
||||
con.PrintErrorf("No selection\n")
|
||||
return
|
||||
}
|
||||
if _, ok := tableStyles[style]; ok {
|
||||
con.Settings.TableStyle = style
|
||||
} else {
|
||||
con.PrintErrorf("Invalid style\n")
|
||||
}
|
||||
}
|
||||
|
||||
// SettingsSaveCmd - The client settings command
|
||||
func SettingsSaveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if con.Settings == nil {
|
||||
con.Settings, err = assets.LoadSettings()
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = assets.SaveSettings(con.Settings)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Settings saved to disk\n")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package settings
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
var (
|
||||
tableStyles = map[string]table.Style{
|
||||
// Sliver styles
|
||||
SliverDefault.Name: SliverDefault,
|
||||
|
||||
// Go Pretty styles
|
||||
table.StyleBold.Name: table.StyleBold,
|
||||
table.StyleColoredBright.Name: table.StyleColoredBright,
|
||||
table.StyleLight.Name: table.StyleLight,
|
||||
table.StyleColoredDark.Name: table.StyleColoredDark,
|
||||
table.StyleColoredBlackOnBlueWhite.Name: table.StyleColoredBlackOnBlueWhite,
|
||||
}
|
||||
|
||||
SliverDefault = table.Style{
|
||||
Name: "SliverDefault",
|
||||
Box: table.BoxStyle{
|
||||
BottomLeft: " ",
|
||||
BottomRight: " ",
|
||||
BottomSeparator: " ",
|
||||
Left: " ",
|
||||
LeftSeparator: " ",
|
||||
MiddleHorizontal: "=",
|
||||
MiddleSeparator: " ",
|
||||
MiddleVertical: " ",
|
||||
PaddingLeft: " ",
|
||||
PaddingRight: " ",
|
||||
Right: " ",
|
||||
RightSeparator: " ",
|
||||
TopLeft: " ",
|
||||
TopRight: " ",
|
||||
TopSeparator: " ",
|
||||
UnfinishedRow: "~~",
|
||||
},
|
||||
Color: table.ColorOptions{
|
||||
IndexColumn: text.Colors{},
|
||||
Footer: text.Colors{},
|
||||
Header: text.Colors{},
|
||||
Row: text.Colors{},
|
||||
RowAlternate: text.Colors{},
|
||||
},
|
||||
Format: table.FormatOptions{
|
||||
Footer: text.FormatDefault,
|
||||
Header: text.FormatTitle,
|
||||
Row: text.FormatDefault,
|
||||
},
|
||||
Options: table.Options{
|
||||
DrawBorder: false,
|
||||
SeparateColumns: true,
|
||||
SeparateFooter: false,
|
||||
SeparateHeader: true,
|
||||
SeparateRows: false,
|
||||
},
|
||||
}
|
||||
|
||||
sliverBordersDefault = table.Style{
|
||||
Name: "SliverBordersDefault",
|
||||
Box: table.BoxStyle{
|
||||
BottomLeft: "+",
|
||||
BottomRight: "+",
|
||||
BottomSeparator: "-",
|
||||
Left: "|",
|
||||
LeftSeparator: "+",
|
||||
MiddleHorizontal: "-",
|
||||
MiddleSeparator: "+",
|
||||
MiddleVertical: "|",
|
||||
PaddingLeft: " ",
|
||||
PaddingRight: " ",
|
||||
Right: "|",
|
||||
RightSeparator: "+",
|
||||
TopLeft: "+",
|
||||
TopRight: "+",
|
||||
TopSeparator: "-",
|
||||
UnfinishedRow: "~~",
|
||||
},
|
||||
Color: table.ColorOptions{
|
||||
IndexColumn: text.Colors{},
|
||||
Footer: text.Colors{},
|
||||
Header: text.Colors{},
|
||||
Row: text.Colors{},
|
||||
RowAlternate: text.Colors{},
|
||||
},
|
||||
Format: table.FormatOptions{
|
||||
Footer: text.FormatDefault,
|
||||
Header: text.FormatTitle,
|
||||
Row: text.FormatDefault,
|
||||
},
|
||||
Options: table.Options{
|
||||
DrawBorder: true,
|
||||
SeparateColumns: true,
|
||||
SeparateFooter: false,
|
||||
SeparateHeader: true,
|
||||
SeparateRows: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// GetTableStyle - Get the current table style
|
||||
func GetTableStyle(con *console.SliverConsoleClient) table.Style {
|
||||
if con.Settings == nil {
|
||||
con.Settings, _ = assets.LoadSettings()
|
||||
}
|
||||
if con.Settings != nil {
|
||||
if value, ok := tableStyles[con.Settings.TableStyle]; ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return tableStyles[SliverDefault.Name]
|
||||
}
|
||||
|
||||
// GetTableWithBordersStyle - Get the table style with borders
|
||||
func GetTableWithBordersStyle(con *console.SliverConsoleClient) table.Style {
|
||||
if con.Settings == nil {
|
||||
con.Settings, _ = assets.LoadSettings()
|
||||
}
|
||||
value, ok := tableStyles[con.Settings.TableStyle]
|
||||
if !ok || con.Settings.TableStyle == SliverDefault.Name {
|
||||
return sliverBordersDefault
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetPageSize - Page size for tables
|
||||
func GetPageSize() int {
|
||||
return 10
|
||||
}
|
||||
|
||||
// PagesOf - Return the pages of a table
|
||||
func PagesOf(renderedTable string) [][]string {
|
||||
lines := strings.Split(renderedTable, "\n")
|
||||
if len(lines) < 2 {
|
||||
return [][]string{}
|
||||
}
|
||||
token := lines[0]
|
||||
pages := [][]string{}
|
||||
page := []string{token}
|
||||
for _, line := range lines[1:] {
|
||||
if line != token {
|
||||
page = append(page, line)
|
||||
} else {
|
||||
pages = append(pages, page)
|
||||
page = []string{token}
|
||||
}
|
||||
}
|
||||
pages = append(pages, page)
|
||||
return pages
|
||||
}
|
||||
|
||||
// PaginateTable - Render paginated table to console
|
||||
func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bool, con *console.SliverConsoleClient) {
|
||||
renderedTable := tw.Render()
|
||||
lineCount := strings.Count(renderedTable, "\n")
|
||||
if !overflow {
|
||||
// Only paginate if the number of lines is at least 2x the terminal height
|
||||
width, height, err := terminal.GetSize(0)
|
||||
if err == nil && 2*height < lineCount {
|
||||
if 7 < height {
|
||||
tw.SetPageSize(height - 6)
|
||||
tw.SetAllowedRowLength(width)
|
||||
} else {
|
||||
tw.SetPageSize(2)
|
||||
tw.SetAllowedRowLength(width)
|
||||
}
|
||||
renderedTable = tw.Render()
|
||||
}
|
||||
}
|
||||
|
||||
pages := PagesOf(renderedTable)
|
||||
for pageNumber, page := range pages {
|
||||
if pageNumber+1 < skipPages {
|
||||
continue
|
||||
}
|
||||
for _, line := range page {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
con.Printf("%s\n", line)
|
||||
}
|
||||
con.Println()
|
||||
if interactive {
|
||||
if 1 < len(pages) {
|
||||
if pageNumber+1 < len(pages) {
|
||||
nextPage := false
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("[%d/%d] Continue?", pageNumber+1, len(pages)),
|
||||
}
|
||||
survey.AskOne(prompt, &nextPage)
|
||||
if !nextPage {
|
||||
break
|
||||
}
|
||||
con.Println()
|
||||
} else {
|
||||
con.Printf(console.Bold+"Page [%d/%d]\n", pageNumber+1, len(pages))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if 1 < len(pages) {
|
||||
con.Printf(console.Bold+"Page [%d/%d]\n", pageNumber+1, len(pages))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -41,18 +42,18 @@ const (
|
||||
|
||||
// ShellCmd - Start an interactive shell on the remote system
|
||||
func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !con.IsUserAnAdult() {
|
||||
if !settings.IsUserAnAdult(con) {
|
||||
return
|
||||
}
|
||||
|
||||
shellPath := ctx.Flags.String("shell-path")
|
||||
noPty := ctx.Flags.Bool("no-pty")
|
||||
if con.ActiveSession.Get().OS != linux && con.ActiveSession.Get().OS != darwin {
|
||||
if con.ActiveTarget.GetSession().OS != linux && con.ActiveTarget.GetSession().OS != darwin {
|
||||
noPty = true // Sliver's PTYs are only supported on linux/darwin
|
||||
}
|
||||
runInteractive(ctx, shellPath, noPty, con)
|
||||
@@ -61,7 +62,7 @@ func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Opening shell tunnel (EOF to exit) ...\n\n")
|
||||
session := con.ActiveSession.Get()
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
@@ -80,7 +81,7 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
|
||||
tunnel := core.Tunnels.Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
|
||||
|
||||
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: shellPath,
|
||||
EnablePTY: !noPty,
|
||||
TunnelID: tunnel.ID,
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package socks
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// SocksAddCmd - Add a new tunneled port forward
|
||||
func SocksAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// listener
|
||||
host := ctx.Flags.String("host")
|
||||
port := ctx.Flags.String("port")
|
||||
bindAddr := fmt.Sprintf("%s:%s", host, port)
|
||||
ln, err := net.Listen("tcp", bindAddr)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Socks5 Listen %s \n", err.Error())
|
||||
return
|
||||
}
|
||||
username := ctx.Flags.String("user")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
password := ctx.Flags.String("password")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
channelProxy := &core.TcpProxy{
|
||||
Rpc: con.Rpc,
|
||||
Session: session,
|
||||
Listener: ln,
|
||||
BindAddr: bindAddr,
|
||||
Username: username,
|
||||
Password: password,
|
||||
KeepAlivePeriod: 60 * time.Second,
|
||||
DialTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
go func(channelProxy *core.TcpProxy) {
|
||||
core.SocksProxies.Start(channelProxy)
|
||||
}(core.SocksProxies.Add(channelProxy).ChannelProxy)
|
||||
con.PrintInfof("Started SOCKS5 %s %s %s %s\n", host, port, username, password)
|
||||
con.PrintWarnf("In-band SOCKS proxies can be a little unstable depending on protocol\n")
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package socks
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// SocksRmCmd - Remove an existing tunneled port forward
|
||||
func SocksRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
socksID := ctx.Flags.Int("id")
|
||||
if socksID < 1 {
|
||||
con.PrintErrorf("Must specify a valid portfwd id\n")
|
||||
return
|
||||
}
|
||||
found := core.SocksProxies.Remove(socksID)
|
||||
if !found {
|
||||
con.PrintErrorf("No portfwd with id %d\n", socksID)
|
||||
} else {
|
||||
con.PrintInfof("Removed portfwd\n")
|
||||
}
|
||||
|
||||
// close
|
||||
con.Rpc.CloseSocks(context.Background(), &sliverpb.Socks{})
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package socks
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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 (
|
||||
"sort"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// SocksCmd - Display information about tunneled port forward(s)
|
||||
func SocksCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
socks := core.SocksProxies.List()
|
||||
if len(socks) == 0 {
|
||||
con.PrintInfof("No socks5 proxies\n")
|
||||
return
|
||||
}
|
||||
sort.Slice(socks[:], func(i, j int) bool {
|
||||
return socks[i].ID < socks[j].ID
|
||||
})
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Session ID",
|
||||
"Bind Address",
|
||||
"Username",
|
||||
"Passwords",
|
||||
})
|
||||
for _, p := range socks {
|
||||
tw.AppendRow(table.Row{p.ID, p.SessionID, p.BindAddr, p.Username, p.Password})
|
||||
}
|
||||
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
Loaded 100 of 871 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user