Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 99 additions & 1 deletion client/command/beacons/beacons.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,39 @@ import (
"golang.org/x/term"

"github.com/bishopfox/sliver/client/command/kill"
"github.com/bishopfox/sliver/client/command/output"
"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"
)

// BeaconResult represents a single beacon in structured output.
type BeaconResult struct {
ID string `json:"id" yaml:"id"`
Name string `json:"name" yaml:"name"`
Transport string `json:"transport" yaml:"transport"`
RemoteAddress string `json:"remoteAddress" yaml:"remoteAddress"`
Hostname string `json:"hostname" yaml:"hostname"`
Username string `json:"username" yaml:"username"`
Process string `json:"process" yaml:"process"`
PID uint32 `json:"pid" yaml:"pid"`
OS string `json:"os" yaml:"os"`
Arch string `json:"arch" yaml:"arch"`
Locale string `json:"locale" yaml:"locale"`
Integrity string `json:"integrity,omitempty" yaml:"integrity,omitempty"`
LastCheckin int64 `json:"lastCheckin" yaml:"lastCheckin"`
NextCheckin int64 `json:"nextCheckin" yaml:"nextCheckin"`
TasksCount uint32 `json:"tasksCount" yaml:"tasksCount"`
TasksCountCompleted uint32 `json:"tasksCountCompleted" yaml:"tasksCountCompleted"`
Active bool `json:"active" yaml:"active"`
}

// BeaconListResult represents the beacons list in structured output.
type BeaconListResult struct {
Beacons []BeaconResult `json:"beacons" yaml:"beacons"`
}

// BeaconsCmd - Display/interact with beacons
func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
killFlag, _ := cmd.Flags().GetString("kill")
Expand Down Expand Up @@ -103,7 +130,13 @@ func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintErrorf("%s\n", err)
return
}
PrintBeacons(beacons.Beacons, filter, filterRegex, con)

format := output.GetOutputFormat(cmd)
if format != output.FormatText {
PrintBeaconsStructured(beacons.Beacons, filter, filterRegex, con, format)
} else {
PrintBeacons(beacons.Beacons, filter, filterRegex, con)
}
}

// PrintBeacons - Display a list of beacons
Expand All @@ -116,6 +149,71 @@ func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp
con.Printf("%s\n", tw.Render())
}

// PrintBeaconsStructured prints beacons in JSON or YAML format.
func PrintBeaconsStructured(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient, format output.OutputFormat) {
result := BeaconListResult{
Beacons: make([]BeaconResult, 0),
}

for _, beacon := range beacons {
username := strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")

// Apply filters
if filter != "" || filterRegex != nil {
match := false
fields := []string{
beacon.ID, beacon.Name, beacon.Transport, beacon.RemoteAddress,
beacon.Hostname, username, beacon.Filename, beacon.OS, beacon.Arch,
}
if filter != "" {
for _, field := range fields {
if strings.Contains(field, filter) {
match = true
break
}
}
}
if !match && filterRegex != nil {
for _, field := range fields {
if filterRegex.MatchString(field) {
match = true
break
}
}
}
if !match {
continue
}
}

br := BeaconResult{
ID: beacon.ID,
Name: beacon.Name,
Transport: beacon.Transport,
RemoteAddress: beacon.RemoteAddress,
Hostname: beacon.Hostname,
Username: username,
Process: beacon.Filename,
PID: beacon.PID,
OS: beacon.OS,
Arch: beacon.Arch,
Locale: beacon.Locale,
Integrity: beacon.Integrity,
LastCheckin: beacon.LastCheckin,
NextCheckin: beacon.NextCheckin,
TasksCount: beacon.TasksCount,
TasksCountCompleted: beacon.TasksCountCompleted,
Active: con.ActiveTarget.GetBeacon() != nil && con.ActiveTarget.GetBeacon().ID == beacon.ID,
}

result.Beacons = append(result.Beacons, br)
}

if err := output.PrintStructured(result, format); err != nil {
con.PrintErrorf("Failed to format output: %s\n", err)
}
}

func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) table.Writer {
width, _, err := term.GetSize(0)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions client/command/beacons/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/command/output"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/commonpb"
Expand Down Expand Up @@ -39,6 +40,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
f.StringP("filter", "f", "", "filter beacons by substring")
f.StringP("filter-re", "e", "", "filter beacons by regular expression")
})
output.BindOutputFlags(beaconsCmd)
flags.BindFlagCompletions(beaconsCmd, func(comp *carapace.ActionMap) {
(*comp)["kill"] = BeaconIDCompleter(con)
(*comp)["interact"] = BeaconIDCompleter(con)
Expand Down
2 changes: 2 additions & 0 deletions client/command/creds/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/bishopfox/sliver/client/command/completers"
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/command/output"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/rsteube/carapace"
Expand All @@ -25,6 +26,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
flags.Bind("creds", true, credsCmd, func(f *pflag.FlagSet) {
f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds")
})
output.BindOutputFlags(credsCmd)

credsAddCmd := &cobra.Command{
Use: consts.AddStr,
Expand Down
51 changes: 50 additions & 1 deletion client/command/creds/creds.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"strings"

"github.com/bishopfox/sliver/client/command/output"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
Expand All @@ -32,6 +33,23 @@ import (
"github.com/spf13/cobra"
)

// CredentialResult represents a single credential in structured output.
type CredentialResult struct {
ID string `json:"id" yaml:"id"`
Collection string `json:"collection" yaml:"collection"`
Username string `json:"username" yaml:"username"`
Plaintext string `json:"plaintext,omitempty" yaml:"plaintext,omitempty"`
Hash string `json:"hash,omitempty" yaml:"hash,omitempty"`
HashType string `json:"hashType,omitempty" yaml:"hashType,omitempty"`
IsCracked bool `json:"isCracked" yaml:"isCracked"`
OriginHostUUID string `json:"originHostUuid,omitempty" yaml:"originHostUuid,omitempty"`
}

// CredentialListResult represents the credentials list in structured output.
type CredentialListResult struct {
Credentials []CredentialResult `json:"credentials" yaml:"credentials"`
}

// CredsCmd - Manage credentials.
func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
Expand All @@ -43,7 +61,13 @@ func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
con.PrintInfof("No credentials 🙁\n")
return
}
PrintCreds(creds.Credentials, con)

format := output.GetOutputFormat(cmd)
if format != output.FormatText {
PrintCredsStructured(creds.Credentials, con, format)
} else {
PrintCreds(creds.Credentials, con)
}
}

func PrintCreds(creds []*clientpb.Credential, con *console.SliverClient) {
Expand Down Expand Up @@ -132,6 +156,31 @@ func CredsCollectionCompleter(con *console.SliverClient) carapace.Action {
})
}

// PrintCredsStructured prints credentials in JSON or YAML format.
func PrintCredsStructured(creds []*clientpb.Credential, con *console.SliverClient, format output.OutputFormat) {
result := CredentialListResult{
Credentials: make([]CredentialResult, 0),
}

for _, cred := range creds {
cr := CredentialResult{
ID: cred.ID,
Collection: cred.Collection,
Username: cred.Username,
Plaintext: cred.Plaintext,
Hash: cred.Hash,
HashType: cred.HashType,
IsCracked: cred.IsCracked,
OriginHostUUID: cred.OriginHostUUID,
}
result.Credentials = append(result.Credentials, cr)
}

if err := output.PrintStructured(result, format); err != nil {
con.PrintErrorf("Failed to format output: %s\n", err)
}
}

// CredsCredentialIDCompleter completes credential IDs.
func CredsCredentialIDCompleter(con *console.SliverClient) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
Expand Down
2 changes: 2 additions & 0 deletions client/command/loot/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package loot

import (
"github.com/bishopfox/sliver/client/command/completers"
"github.com/bishopfox/sliver/client/command/output"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -29,6 +30,7 @@ func Commands(con *console.SliverClient) []*cobra.Command {
flags.Bind("loot", false, lootCmd, func(f *pflag.FlagSet) {
f.StringP("filter", "f", "", "filter based on loot type")
})
output.BindOutputFlags(lootCmd)

lootAddCmd := &cobra.Command{
Use: consts.LootLocalStr,
Expand Down
56 changes: 55 additions & 1 deletion client/command/loot/loot.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"

"github.com/bishopfox/sliver/client/command/output"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/forms"
Expand All @@ -37,14 +38,35 @@ import (
"github.com/bishopfox/sliver/util"
)

// LootItemResult represents a single loot item in structured output.
type LootItemResult struct {
ID string `json:"id" yaml:"id"`
Name string `json:"name" yaml:"name"`
FileName string `json:"fileName" yaml:"fileName"`
FileType string `json:"fileType" yaml:"fileType"`
Size int64 `json:"size" yaml:"size"`
SizeH string `json:"sizeHuman" yaml:"sizeHuman"`
}

// LootListResult represents the loot list in structured output.
type LootListResult struct {
Items []LootItemResult `json:"items" yaml:"items"`
}

// LootCmd - The loot root command
func LootCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
allLoot, err := con.Rpc.LootAll(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Failed to fetch loot %s\n", err)
return
}
PrintAllFileLootTable(allLoot, con)

format := output.GetOutputFormat(cmd)
if format != output.FormatText {
PrintLootStructured(allLoot, con, format)
} else {
PrintAllFileLootTable(allLoot, con)
}
}

// PrintAllFileLootTable - Displays a table of all file loot
Expand Down Expand Up @@ -212,3 +234,35 @@ func isText(sample []byte) bool {
}
return true
}

// PrintLootStructured prints loot in JSON or YAML format.
func PrintLootStructured(allLoot *clientpb.AllLoot, con *console.SliverClient, format output.OutputFormat) {
result := LootListResult{
Items: make([]LootItemResult, 0),
}

if allLoot == nil || len(allLoot.Loot) == 0 {
if err := output.PrintStructured(result, format); err != nil {
con.PrintErrorf("Failed to format output: %s\n", err)
}
return
}

for _, loot := range allLoot.Loot {
if loot.File != nil {
item := LootItemResult{
ID: loot.ID,
Name: loot.Name,
FileName: loot.File.Name,
FileType: fileTypeToStr(loot.FileType),
Size: loot.Size,
SizeH: util.ByteCountBinary(loot.Size),
}
result.Items = append(result.Items, item)
}
}

if err := output.PrintStructured(result, format); err != nil {
con.PrintErrorf("Failed to format output: %s\n", err)
}
}
Loading