Compare commits

...
Author SHA1 Message Date
rkervella 0aee9644e1 Add missing Stderr 2022-07-13 07:38:58 -07:00
Joe 68b129acaa Merge pull request #740 from BishopFox/fix/shell-stderr
Support multiple `io.ReadCloser` in `transports.Tunnel`
2022-07-10 21:48:14 -05:00
rkervella e9d0f23152 Use a ReadCloser slice in transports.Tunnel 2022-07-10 11:00:38 -07:00
Joe 1099a4acb7 Merge pull request #739 from BishopFox/fix/implant-name
Fix regression from #737
2022-07-10 10:48:16 -07:00
rkervella a51c318807 Fix regression from #737 2022-07-10 09:40:09 -07:00
rkervella e7284b5880 Handle stderr for shell command 2022-07-10 09:33:42 -07:00
rkervella 0b9cf0f105 Remove raw data print 2022-07-10 09:26:43 -07:00
rkervella 8350b441ca Remove raw dump of session data 2022-07-10 09:22:04 -07:00
rkervella f5646d9975 Fix flag parsing for implant name 2022-07-10 09:15:49 -07:00
Joe ad312b517a Merge pull request #737 from Marshall-Hallenbeck/master
Allow additional characters in implant, session, and beacon names
2022-07-09 20:31:39 -07:00
Marshall Hallenbeck 253197f21a Allow for _-. characters in session & beacon names; closes #738 2022-07-09 21:24:42 -04:00
Marshall Hallenbeck 31e0ad2967 use actual util AllowedName function in server side check for #735 2022-07-09 21:06:32 -04:00
Marshall Hallenbeck f83007fe19 Allow additional characters in implant names; closes #735 2022-07-09 20:46:02 -04:00
Joe e3e5016ed0 Merge pull request #734 from BishopFox/fix/named-pipe-perms
Fix/named pipe perms
2022-07-07 18:31:52 -07:00
rkervella fb4e05617c Merge branch 'master' into fix/named-pipe-perms 2022-07-07 17:07:22 -07:00
rkervella 5a781500ee Fix [SMB Named Pipe] ACL is too restrictive on pivot named pipe #732 2022-07-07 17:05:34 -07:00
Joe fbef52a72d Merge pull request #731 from RafBishopFox/master
Adding Filtering to the Download Command
2022-07-07 15:25:49 -05:00
RafBishopFox a018ba6a11 client/download: use filepath.Base to prevent directory traversal 2022-07-07 11:18:43 -04:00
RafBishopFox fb38c6e4bb download command: Closing files after adding them to the archive 2022-07-07 10:44:15 -04:00
RafBishopFox c989e2970d Fixing protobuf for backwards compatibility 2022-07-07 10:29:33 -04:00
RafBishopFox d935bfb8b3 Adding filtering to the download command 2022-07-07 09:17:46 -04:00
Joe 814670dc6d Merge pull request #727 from RafBishopFox/master
Moving filtering functionality of ls to the implant
2022-06-23 16:23:48 -05:00
Raf 140c47e163 Merge branch 'BishopFox:master' into master 2022-06-23 12:50:53 -04:00
RafBishopFox e28b062e6c Moving ls filtering fuctionality to the implant, add item count and total size to ls output, resolve symlinks in ls output 2022-06-23 09:57:23 -04:00
moloch-- be7b723c8a Spelling fix 2022-06-21 08:24:22 -07:00
Joe 6be3ff8db2 Merge pull request #725 from BishopFox/fix/tun-handlers-errors
Add error reporting on failure for certain tunnel handlers.
2022-06-21 09:05:17 -05:00
rkervella 20520755b2 Add client side error handling 2022-06-20 20:22:43 -07:00
rkervella 0f7b6c0f35 Report errors on failure for shell and portfwd 2022-06-20 19:57:50 -07:00
Joe 0c7c2e0291 Merge pull request #724 from BishopFox/fix/alias-install-path
Fix alias installation directory for manual installs
2022-06-20 18:45:02 -05:00
rkervella 709a5fa453 Use CommandName as base directory 2022-06-20 15:23:17 -07:00
Joe 5e4ec91914 Merge pull request #722 from BishopFox/fix/execute-assembly
Fix argument parsing in execute-assembly to remove trailing spaces
2022-06-20 13:09:07 -05:00
rkervella a19ec2b348 Fix argument parsing in execute-assembly to remove trailing spaces 2022-06-20 10:24:50 -07:00
Joe 9118f7423f Merge pull request #716 from BishopFox/fix/netstack-regression
Fix regression: add back IP forwarding for wireguard tunnels.
2022-06-15 09:59:09 -05:00
41 changed files with 1575 additions and 1074 deletions

No files matched your search

+2 -2
View File
@@ -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)
+2
View File
@@ -269,6 +269,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
} else {
extArgs = strings.Join(args, " ")
}
extArgs = strings.TrimSpace(extArgs)
entryPoint := aliasManifest.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
+1 -1
View File
@@ -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"`
+2 -1
View File
@@ -1799,6 +1799,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 +2484,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 {
+4 -1
View File
@@ -58,13 +58,16 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
assemblyArgs := ctx.Args.StringList("arguments")
process := ctx.Flags.String("process")
assemblyArgsStr := strings.Join(assemblyArgs, " ")
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
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, " "),
Arguments: assemblyArgsStr,
Assembly: assemblyBytes,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
+74 -2
View File
@@ -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,23 @@ 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
fileName = fmt.Sprintf("%s_download_%s_%d.tar.gz", filepath.Base(con.GetActiveSessionConfig().Name), 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 +192,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 -66
View File
@@ -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))
}
+5 -8
View File
@@ -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
}
}
+20 -1
View File
@@ -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.`
+3
View File
@@ -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)
+4 -8
View File
@@ -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
+13 -1
View File
@@ -82,7 +82,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 +93,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)
+1 -1
View File
@@ -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{}
+10
View File
@@ -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
+278 -65
View File
@@ -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
}
+1 -1
View File
@@ -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)
@@ -22,8 +22,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}}
@@ -13,6 +13,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 +25,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 +50,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 +114,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)
@@ -10,6 +10,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 +23,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,6 +38,12 @@ 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
}
@@ -39,6 +52,12 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
// {{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 +67,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 +101,35 @@ func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connect
}
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
_, err := io.Copy(tWriter, tunnel.Reader)
for _, rc := range tunnel.Readers {
go func(outErr io.ReadCloser) {
tWriter := tunnelWriter{
conn: connection,
tun: tunnel,
}
_, 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)
@@ -27,7 +27,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,13 @@
package tunnel_handlers
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,
}
}
+12 -3
View File
@@ -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
+3 -2
View File
@@ -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
+1 -1
View File
@@ -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}}
+11
View File
@@ -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
}
@@ -110,6 +120,7 @@ func ptyShell(tunnelID uint64, command []string) (*Shell, error) {
Command: cmd,
Stdout: term,
Stdin: term,
Stderr: term,
Cancel: cancel,
}, err
}
+1
View File
@@ -31,6 +31,7 @@ type Shell struct {
Command *exec.Cmd
Stdout io.ReadCloser
Stdin io.WriteCloser
Stderr io.ReadCloser
Cancel context.CancelFunc
}
+10
View File
@@ -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
}
+10
View File
@@ -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
}
-3
View File
@@ -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
+10 -7
View File
@@ -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,8 @@ func (c *Tunnel) IncWriteSequence() {
// Close - close tunnel reader and writer
func (c *Tunnel) Close() {
c.Reader.Close()
for _, rc := range c.Readers {
rc.Close()
}
c.Writer.Close()
}
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.20.1
// protoc-gen-go v1.26.0
// protoc v3.19.4
// source: clientpb/client.proto
package clientpb
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.20.1
// protoc-gen-go v1.26.0
// protoc v3.19.4
// source: dnspb/dns.proto
package dnspb
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.20.1
// protoc-gen-go v1.26.0
// protoc v3.19.4
// source: rpcpb/services.proto
package rpcpb
-4
View File
@@ -1,8 +1,4 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.20.1
// source: rpcpb/services.proto
package rpcpb
File diff suppressed because it is too large. Load diff
+5
View File
@@ -170,6 +170,7 @@ message FileInfo {
int64 Size = 3;
int64 ModTime = 4;
string Mode = 5;
string Link = 6;
}
message CdReq {
@@ -231,6 +232,7 @@ message DownloadReq {
string Path = 1;
int64 Start = 2;
int64 Stop = 3;
bool Recurse = 4;
commonpb.Request Request = 9;
}
@@ -243,6 +245,8 @@ message Download {
int64 Stop = 5;
bytes Data = 6;
bool IsDir = 7;
int32 ReadFiles = 8;
int32 UnreadableFiles = 10;
commonpb.Response Response = 9;
}
@@ -761,6 +765,7 @@ enum PivotType {
message PivotStartListenerReq {
PivotType Type = 1;
string BindAddress = 2;
repeated bool Options = 3;
commonpb.Request Request = 9;
}
+4 -6
View File
@@ -29,7 +29,6 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"text/template"
@@ -194,11 +193,10 @@ func ImplantConfigFromProtobuf(pbConfig *clientpb.ImplantConfig) (string, *model
}
name := ""
if pbConfig.Name != "" {
// Only allow user-provided alpha/numeric names
if regexp.MustCompile(`^[[:alnum:]]+$`).MatchString(pbConfig.Name) {
name = pbConfig.Name
}
if err := util.AllowedName(pbConfig.Name); err != nil {
buildLog.Warnf("%s\n", err)
} else {
name = pbConfig.Name
}
return name, cfg
}
+1 -1
View File
@@ -40,5 +40,5 @@ var (
ErrDatabaseFailure = status.Error(codes.Internal, "Database operation failed")
// ErrInvalidName - Invalid name
ErrInvalidName = status.Error(codes.InvalidArgument, "Invalid session name, alphanumerics only")
ErrInvalidName = status.Error(codes.InvalidArgument, "Invalid session name, alphanumerics and _-. only")
)
+2 -3
View File
@@ -20,13 +20,12 @@ package rpc
import (
"context"
"regexp"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/server/core"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/util"
)
const maxNameLength = 32
@@ -56,7 +55,7 @@ func (rpc *Server) Rename(ctx context.Context, req *clientpb.RenameReq) (*common
if len(req.Name) < 1 || maxNameLength < len(req.Name) {
return resp, ErrInvalidName
}
if !regexp.MustCompile(`^[[:alnum:]]+$`).MatchString(req.Name) {
if err := util.AllowedName(req.Name); err != nil {
return resp, ErrInvalidName
}
+42
View File
@@ -0,0 +1,42 @@
package util
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"errors"
"regexp"
)
func AllowedName(name string) error {
if name != "" {
// allow for alphanumeric, periods, dashes, and underscores in name
isAllowed := regexp.MustCompile(`^[[:alnum:]\.\-_]+$`).MatchString
// do not allow for files ".", "..", or anything starting with ".."
additionalDeny := regexp.MustCompile(`^\.\.|^\.$`).MatchString
if !isAllowed(name) {
return errors.New("Name must be alphanumeric or .-_ only\n")
} else if additionalDeny(name) {
return errors.New("Name cannot be \".\", \"..\", or start with \"..\"")
} else {
return nil
}
} else {
return errors.New("Name cannot be blank!")
}
}
+40
View File
@@ -0,0 +1,40 @@
package util
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"testing"
)
func TestAllowedName(t *testing.T) {
notAllowed := [3]string{".", "..", "..test"}
isAllowed := [6]string{"test", "testing_string", "testing.string", "testing-string", "testing..string", "test.."}
for i := 0; i < len(notAllowed); i++ {
if err := AllowedName(notAllowed[i]); err == nil {
t.Fatalf("failed to deny non allowed implant name")
}
}
for i := 0; i < len(isAllowed); i++ {
if err := AllowedName(isAllowed[i]); err != nil {
t.Fatalf("failed to allow allowed implant name")
}
}
}