mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb5303ef04 | ||
|
|
d6a7c024f8 | ||
|
|
2adf0e8cb0 | ||
|
|
c2cacb5e1e | ||
|
|
d7e0293647 | ||
|
|
82d500ceec | ||
|
|
a3f5dcd54a | ||
|
|
37ff504164 | ||
|
|
552093c7b6 | ||
|
|
7a143c58e6 |
No files matched your search
@@ -26,7 +26,6 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
@@ -70,7 +69,7 @@ func GetConfigs() map[string]*ClientConfig {
|
||||
|
||||
confs := map[string]*ClientConfig{}
|
||||
for _, confFile := range configFiles {
|
||||
confFilePath := path.Join(configDir, confFile.Name())
|
||||
confFilePath := filepath.Join(configDir, confFile.Name())
|
||||
log.Printf("Parsing config %s", confFilePath)
|
||||
|
||||
conf, err := ReadConfig(confFilePath)
|
||||
|
||||
+21
-9
@@ -35,12 +35,33 @@ import (
|
||||
|
||||
const (
|
||||
logFileName = "sliver-client.log"
|
||||
|
||||
configFlagStr = "config"
|
||||
preludeServerFlagStr = "server"
|
||||
rangeFlagStr = "range"
|
||||
aesKeyFlagStr = "aes-key"
|
||||
)
|
||||
|
||||
var (
|
||||
sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
// Import
|
||||
rootCmd.AddCommand(cmdImport)
|
||||
|
||||
// Prelude
|
||||
cmdPrelude.Flags().StringP(configFlagStr, "c", "", "sliver client config file path")
|
||||
cmdPrelude.Flags().StringP(preludeServerFlagStr, "s", "", "prelude server connection string")
|
||||
cmdPrelude.Flags().StringP(rangeFlagStr, "r", "", "prelude range")
|
||||
cmdPrelude.Flags().StringP(aesKeyFlagStr, "a", "", "prelude range")
|
||||
rootCmd.AddCommand(cmdPrelude)
|
||||
|
||||
// Version
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
}
|
||||
|
||||
// Initialize logging
|
||||
func initLogging(appDir string) *os.File {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
@@ -52,15 +73,6 @@ func initLogging(appDir string) *os.File {
|
||||
return logFile
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
// Import
|
||||
rootCmd.AddCommand(cmdImport)
|
||||
|
||||
// Version
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "sliver-client",
|
||||
Short: "",
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/prelude"
|
||||
"github.com/bishopfox/sliver/client/transport"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdPrelude = &cobra.Command{
|
||||
Use: "prelude",
|
||||
Short: "Start client as a Prelude bridge",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
confFilePath, err := cmd.Flags().GetString("config")
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
config, err := assets.ReadConfig(confFilePath)
|
||||
if err != nil {
|
||||
log.Printf("[!] Failed to read config file: %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
preludeServer, err := cmd.Flags().GetString(preludeServerFlagStr)
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
agentRange, err := cmd.Flags().GetString(rangeFlagStr)
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
aesKey, err := cmd.Flags().GetString(aesKeyFlagStr)
|
||||
if err != nil {
|
||||
log.Printf("[!] %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Prelude relay mode, connecting to %s ... ", preludeServer)
|
||||
log.Printf("Prelude relay mode, connecting to %s ...", preludeServer)
|
||||
rpc, _, err := transport.MTLSConnect(config)
|
||||
if err != nil {
|
||||
log.Printf("[!] Failed to connect to server %s", err)
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Println("success!")
|
||||
log.Printf("Connection successful")
|
||||
preludeConfig := &prelude.OperatorConfig{
|
||||
Range: agentRange,
|
||||
OperatorURL: preludeServer,
|
||||
RPC: rpc,
|
||||
AESKey: aesKey,
|
||||
}
|
||||
implantMapper := prelude.InitImplantMapper(preludeConfig)
|
||||
sessions, err := rpc.GetSessions(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not get session list: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if len(sessions.Sessions) > 0 {
|
||||
log.Printf("Adding existing sessions ...")
|
||||
for _, session := range sessions.Sessions {
|
||||
if !session.IsDead {
|
||||
err = implantMapper.AddImplant(session, nil)
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not add session %s to implant mapper: %s", session.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf("Done !")
|
||||
}
|
||||
beacons, err := rpc.GetBeacons(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not get beacon list: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
if len(beacons.Beacons) > 0 {
|
||||
log.Printf("Adding existing beacons ...")
|
||||
|
||||
// I'm not sure this callback map is actually needed
|
||||
type BeaconTaskCallback func(*clientpb.BeaconTask)
|
||||
beaconTaskCallbacks := map[string]BeaconTaskCallback{}
|
||||
beaconTaskCallbacksMutex := &sync.Mutex{}
|
||||
|
||||
for _, beacon := range beacons.Beacons {
|
||||
err = implantMapper.AddImplant(beacon, func(taskID string, cb func(task *clientpb.BeaconTask)) {
|
||||
beaconTaskCallbacksMutex.Lock()
|
||||
defer beaconTaskCallbacksMutex.Unlock()
|
||||
beaconTaskCallbacks[taskID] = cb
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[!] Could not add beacon %s to implant mapper: %s", beacon.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
Prelude
|
||||
=======
|
||||
|
||||
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
|
||||
The `prelude` package handles the connection with [Prelude's Operator platform](https://www.prelude.org/).
|
||||
|
||||
## BOF support
|
||||
|
||||
To execute BOF TTPs, use the custom `bof` executor:
|
||||
|
||||
```json
|
||||
bof.[{"type": "int", "value": #{json.T1234.ProcessId}}]
|
||||
```
|
||||
+52
-42
@@ -34,52 +34,62 @@ func RunCommand(message string, executor string, payload []byte, agentSession *O
|
||||
switch executor {
|
||||
case "keyword":
|
||||
task := splitMessage(message, '.')
|
||||
switch task[0] {
|
||||
case "bof":
|
||||
if len(task) != 2 {
|
||||
break
|
||||
}
|
||||
var bargs []bofArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &bargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "execute-assembly":
|
||||
if len(task) < 2 {
|
||||
break
|
||||
}
|
||||
var eaargs execasmArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &eaargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "exit":
|
||||
return shutdown(agentSession)
|
||||
default:
|
||||
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return processKeywordExecutor(task, payload, agentSession, onFinish)
|
||||
case "bof", "extension":
|
||||
// can be either BOF or regular extension
|
||||
return runExtension(message, payload, agentSession.Implant, agentSession.RPC, onFinish)
|
||||
default:
|
||||
bites, status, pid := execute(message, executor, agentSession, onFinish)
|
||||
return string(bites), status, pid
|
||||
}
|
||||
}
|
||||
|
||||
func processKeywordExecutor(task []string, payload []byte, agentSession *OperatorImplantBridge, onFinish func(string, int, int)) (string, int, int) {
|
||||
if len(task) == 0 {
|
||||
return "", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
switch task[0] {
|
||||
case "bof":
|
||||
if len(task) != 2 {
|
||||
break
|
||||
}
|
||||
var bargs []bofArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &bargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing BOF file", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(agentSession.Implant, agentSession.RPC, payload, bargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "execute-assembly":
|
||||
if len(task) < 2 {
|
||||
break
|
||||
}
|
||||
var eaargs execasmArgs
|
||||
argStr := strings.ReplaceAll(task[1], `\`, `\\`)
|
||||
err := json.Unmarshal([]byte(argStr), &eaargs)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if payload == nil {
|
||||
return "missing .NET assembly", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := execAsm(agentSession.Implant, agentSession.RPC, payload, eaargs, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, 0, 0
|
||||
case "exit":
|
||||
return shutdown(agentSession)
|
||||
default:
|
||||
return "Keyword selected not available for agent", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return "", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package prelude
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
)
|
||||
|
||||
//{"Name":"coff-loader", "ServerStore":false, "Args":"PAYLOAD.BYTES", "Export":"LoadAndRun", "arguments": [{"type": "wstring", "value": "C:\Users\lab\Downloads\RELIEVED_TV.exe"}]}
|
||||
type extensionMessage struct {
|
||||
Name string `json:"Name"`
|
||||
ServerStore bool `json:"ServerStore"`
|
||||
Args string `json:"Args"`
|
||||
Export string `json:"Export"`
|
||||
Arguments []bofArgs `json:"arguments"`
|
||||
}
|
||||
|
||||
func runExtension(message string, payload []byte, activeImplant ActiveImplant, rpc rpcpb.SliverRPCClient, onFinish func(string, int, int)) (string, int, int) {
|
||||
var msg extensionMessage
|
||||
err := json.Unmarshal([]byte(message), &msg)
|
||||
if err != nil {
|
||||
println(message)
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
if msg.Args == "PAYLOAD.BYTES" {
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
out, err := runBOF(activeImplant, rpc, payload, msg.Arguments, onFinish)
|
||||
if err != nil {
|
||||
return err.Error(), ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
return out, SuccessExitStatus, SuccessExitStatus
|
||||
}
|
||||
return "Unsupported extension", ErrorExitStatus, ErrorExitStatus
|
||||
}
|
||||
@@ -112,7 +112,6 @@ func (p *OperatorImplantMapper) AddImplant(a ActiveImplant, callback func(string
|
||||
if p.conf.AESKey == "" {
|
||||
return errors.New("missing AES key")
|
||||
}
|
||||
a.GetID()
|
||||
encryptionKey := p.conf.AESKey
|
||||
agentConfig := AgentConfig{
|
||||
Name: a.GetID(),
|
||||
|
||||
@@ -21,7 +21,7 @@ package util
|
||||
func DetermineExecutors(platform string, arch string) []string {
|
||||
platformExecutors := map[string]map[string][]string{
|
||||
"windows": {
|
||||
"file": {"pwsh.exe", "powershell.exe", "cmd.exe", "", ""},
|
||||
"file": {"powershell.exe", "powershell.exe", "cmd.exe", "", ""},
|
||||
"executor": {"pwsh", "psh", "cmd", "bof", "exec"},
|
||||
},
|
||||
"linux": {
|
||||
|
||||
Reference in new issue
Block a user