mirror of
https://github.com/BishopFox/sliver
synced 2026-06-08 10:29:05 +00:00
287 lines
8.2 KiB
Go
287 lines
8.2 KiB
Go
package use
|
|
|
|
/*
|
|
Sliver Implant Framework
|
|
Copyright (C) 2021 Bishop Fox
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"text/tabwriter"
|
|
|
|
"github.com/rsteube/carapace"
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/bishopfox/sliver/client/command/beacons"
|
|
"github.com/bishopfox/sliver/client/console"
|
|
"github.com/bishopfox/sliver/client/forms"
|
|
"github.com/bishopfox/sliver/protobuf/clientpb"
|
|
"github.com/bishopfox/sliver/protobuf/commonpb"
|
|
)
|
|
|
|
var ErrNoSelection = errors.New("no selection")
|
|
|
|
func registerUseIDCompletion(cmd *cobra.Command, con *console.SliverClient, includeSessions, includeBeacons bool) {
|
|
if cmd == nil || cmd.ValidArgsFunction != nil {
|
|
return
|
|
}
|
|
|
|
cmd.ValidArgsFunction = func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
if len(args) > 0 {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
values := useCompletionValues(con, includeSessions, includeBeacons)
|
|
return filterCompletionValues(values, toComplete), cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
}
|
|
|
|
func useCompletionValues(con *console.SliverClient, includeSessions, includeBeacons bool) []string {
|
|
results := []string{}
|
|
if con == nil || con.Rpc == nil {
|
|
return results
|
|
}
|
|
|
|
if includeSessions {
|
|
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
|
if err == nil {
|
|
for _, session := range sessions.Sessions {
|
|
link := fmt.Sprintf("[%s <- %s]", session.ActiveC2, session.RemoteAddress)
|
|
id := fmt.Sprintf("%s (%d)", session.Name, session.PID)
|
|
userHost := fmt.Sprintf("%s@%s", session.Username, session.Hostname)
|
|
desc := strings.Join([]string{id, userHost, link}, " ")
|
|
|
|
results = append(results, fmt.Sprintf("%s\t%s", session.ID[:8], desc))
|
|
}
|
|
}
|
|
}
|
|
|
|
if includeBeacons {
|
|
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
|
if err == nil {
|
|
for _, beacon := range beacons.Beacons {
|
|
link := fmt.Sprintf("[%s <- %s]", beacon.ActiveC2, beacon.RemoteAddress)
|
|
id := fmt.Sprintf("%s (%d)", beacon.Name, beacon.PID)
|
|
userHost := fmt.Sprintf("%s@%s", beacon.Username, beacon.Hostname)
|
|
desc := strings.Join([]string{id, userHost, link}, " ")
|
|
|
|
results = append(results, fmt.Sprintf("%s\t%s", beacon.ID[:8], desc))
|
|
}
|
|
}
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
func filterCompletionValues(values []string, prefix string) []string {
|
|
if prefix == "" {
|
|
return values
|
|
}
|
|
|
|
filtered := make([]string, 0, len(values))
|
|
for _, value := range values {
|
|
candidate := value
|
|
if tab := strings.IndexByte(value, '\t'); tab >= 0 {
|
|
candidate = value[:tab]
|
|
}
|
|
if strings.HasPrefix(candidate, prefix) {
|
|
filtered = append(filtered, value)
|
|
}
|
|
}
|
|
|
|
return filtered
|
|
}
|
|
|
|
// UseCmd - Change the active session
|
|
func UseCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
|
var session *clientpb.Session
|
|
var beacon *clientpb.Beacon
|
|
var err error
|
|
|
|
var idArg string
|
|
if len(args) > 0 {
|
|
idArg = args[0]
|
|
}
|
|
|
|
// idArg := ctx.Args.String("id")
|
|
if idArg != "" {
|
|
session, beacon, err = SessionOrBeaconByID(idArg, con)
|
|
} else {
|
|
session, beacon, err = SelectSessionOrBeacon(con)
|
|
}
|
|
if err != nil {
|
|
con.PrintErrorf("%s\n", err)
|
|
return
|
|
}
|
|
if session != nil {
|
|
con.PrintInfof("Active session %s (%s)\n", session.Name, session.ID)
|
|
con.ActiveTarget.Set(session, nil)
|
|
} else if beacon != nil {
|
|
con.PrintInfof("Active beacon %s (%s)\n", beacon.Name, beacon.ID)
|
|
con.ActiveTarget.Set(nil, beacon)
|
|
}
|
|
}
|
|
|
|
// SessionOrBeaconByID - Select a session or beacon by ID
|
|
func SessionOrBeaconByID(id string, con *console.SliverClient) (*clientpb.Session, *clientpb.Beacon, error) {
|
|
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
} else {
|
|
for _, session := range sessions.Sessions {
|
|
if strings.HasPrefix(session.ID, id) {
|
|
return session, nil, nil
|
|
}
|
|
}
|
|
}
|
|
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for _, beacon := range beacons.Beacons {
|
|
if strings.HasPrefix(beacon.ID, id) {
|
|
return nil, beacon, nil
|
|
}
|
|
}
|
|
return nil, nil, fmt.Errorf("no session or beacon found with ID %s", id)
|
|
}
|
|
|
|
// SelectSessionOrBeacon - Select a session or beacon
|
|
func SelectSessionOrBeacon(con *console.SliverClient) (*clientpb.Session, *clientpb.Beacon, error) {
|
|
// Get and sort sessions
|
|
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
sessionsMap := map[string]*clientpb.Session{}
|
|
for _, session := range sessions.GetSessions() {
|
|
sessionsMap[session.ID] = session
|
|
}
|
|
var sessionKeys []string
|
|
for _, session := range sessions.Sessions {
|
|
sessionKeys = append(sessionKeys, session.ID)
|
|
}
|
|
sort.Strings(sessionKeys)
|
|
|
|
// Get and sort beacons
|
|
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
beaconsMap := map[string]*clientpb.Beacon{}
|
|
for _, beacon := range beacons.Beacons {
|
|
beaconsMap[beacon.ID] = beacon
|
|
}
|
|
beaconKeys := []string{}
|
|
for beaconID := range beaconsMap {
|
|
beaconKeys = append(beaconKeys, beaconID)
|
|
}
|
|
sort.Strings(beaconKeys)
|
|
|
|
if len(beaconKeys) == 0 && len(sessionKeys) == 0 {
|
|
return nil, nil, fmt.Errorf("no sessions or beacons 🙁")
|
|
}
|
|
|
|
// Render selection table
|
|
outputBuf := bytes.NewBufferString("")
|
|
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
|
|
|
for _, key := range sessionKeys {
|
|
session := sessionsMap[key]
|
|
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
|
"SESSION",
|
|
strings.Split(session.ID, "-")[0],
|
|
session.Name,
|
|
session.RemoteAddress,
|
|
session.Hostname,
|
|
session.Username,
|
|
fmt.Sprintf("%s/%s", session.OS, session.Arch),
|
|
)
|
|
}
|
|
for _, key := range beaconKeys {
|
|
beacon := beaconsMap[key]
|
|
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
|
"BEACON",
|
|
strings.Split(beacon.ID, "-")[0],
|
|
beacon.Name,
|
|
beacon.RemoteAddress,
|
|
beacon.Hostname,
|
|
beacon.Username,
|
|
fmt.Sprintf("%s/%s", beacon.OS, beacon.Arch),
|
|
)
|
|
}
|
|
table.Flush()
|
|
|
|
options := strings.Split(outputBuf.String(), "\n")
|
|
options = options[:len(options)-1] // Remove the last empty option
|
|
selected := ""
|
|
_ = forms.Select("Select a session or beacon:", options, &selected)
|
|
if selected == "" {
|
|
return nil, nil, ErrNoSelection
|
|
}
|
|
for index, option := range options {
|
|
if option == selected {
|
|
if index < len(sessionKeys) {
|
|
return sessionsMap[sessionKeys[index]], nil, nil
|
|
}
|
|
return nil, beaconsMap[beaconKeys[index-len(sessionKeys)]], nil
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
// BeaconAndSessionIDCompleter - BeaconAndSessionIDCompleter for beacon / session ids
|
|
func BeaconAndSessionIDCompleter(con *console.SliverClient) carapace.Action {
|
|
comps := func(ctx carapace.Context) carapace.Action {
|
|
var action carapace.Action
|
|
|
|
return action.Invoke(ctx).Merge(
|
|
SessionIDCompleter(con).Invoke(ctx),
|
|
beacons.BeaconIDCompleter(con).Invoke(ctx),
|
|
).ToA()
|
|
}
|
|
|
|
return carapace.ActionCallback(comps)
|
|
}
|
|
|
|
// SessionIDCompleter completes session IDs
|
|
func SessionIDCompleter(con *console.SliverClient) carapace.Action {
|
|
callback := func(_ carapace.Context) carapace.Action {
|
|
results := make([]string, 0)
|
|
|
|
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
|
if err == nil {
|
|
for _, s := range sessions.Sessions {
|
|
link := fmt.Sprintf("[%s <- %s]", s.ActiveC2, s.RemoteAddress)
|
|
id := fmt.Sprintf("%s (%d)", s.Name, s.PID)
|
|
userHost := fmt.Sprintf("%s@%s", s.Username, s.Hostname)
|
|
desc := strings.Join([]string{id, userHost, link}, " ")
|
|
|
|
results = append(results, s.ID[:8])
|
|
results = append(results, desc)
|
|
}
|
|
}
|
|
return carapace.ActionValuesDescribed(results...).Tag("sessions")
|
|
}
|
|
|
|
return carapace.ActionCallback(callback)
|
|
}
|