Skip to content

Commit

Permalink
initial outline of external builders completed
Browse files Browse the repository at this point in the history
  • Loading branch information
moloch-- committed Sep 28, 2022
1 parent dd9bb01 commit 0fab805
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 41 deletions.
71 changes: 62 additions & 9 deletions server/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/server/db/models"
"github.com/bishopfox/sliver/server/generate"
"github.com/bishopfox/sliver/server/log"
)

Expand All @@ -49,7 +51,7 @@ func StartBuilder(rpc rpcpb.SliverRPCClient, conf Config) {

events := buildEvents(rpc)

builderLog.Infof("Started process as external builder")
builderLog.Infof("Successfully started process as external builder")
builder := sliverBuilder{
rpc: rpc,
config: conf,
Expand Down Expand Up @@ -87,6 +89,8 @@ func buildEvents(rpc rpcpb.SliverRPCClient) <-chan *clientpb.Event {
switch event.EventType {
case consts.BuildEvent:
events <- event
default:
builderLog.Debugf("Ignore event (%s)", event.EventType)
}
}
}()
Expand All @@ -97,7 +101,7 @@ func buildEvents(rpc rpcpb.SliverRPCClient) <-chan *clientpb.Event {
func (b *sliverBuilder) HandleBuildEvent(event *clientpb.Event) {
implantConfigID := string(event.Data)
builderLog.Infof("Build event for implant config id: %s", implantConfigID)
implantConf, err := b.rpc.GenerateExternalGetImplantConfig(context.Background(), &clientpb.ImplantConfig{
extConfig, err := b.rpc.GenerateExternalGetImplantConfig(context.Background(), &clientpb.ImplantConfig{
ID: implantConfigID,
})
if err != nil {
Expand All @@ -106,21 +110,70 @@ func (b *sliverBuilder) HandleBuildEvent(event *clientpb.Event) {
}

// check to see if the event matches a target we're configured to build for
if !contains(b.config.GOOSs, implantConf.Config.GOOS) {
builderLog.Warnf("This builder is not configured to build for goos %s, ignore event", implantConf.Config.GOOS)
if !contains(b.config.GOOSs, extConfig.Config.GOOS) {
builderLog.Warnf("This builder is not configured to build for goos %s, ignore event", extConfig.Config.GOOS)
return
}
if !contains(b.config.GOARCHs, implantConf.Config.GOARCH) {
builderLog.Warnf("This builder is not configured to build for goarch %s, ignore event", implantConf.Config.GOARCH)
if !contains(b.config.GOARCHs, extConfig.Config.GOARCH) {
builderLog.Warnf("This builder is not configured to build for goarch %s, ignore event", extConfig.Config.GOARCH)
return
}
if !contains(b.config.Formats, implantConf.Config.Format) {
builderLog.Warnf("This builder is not configured to build for format %s, ignore event", implantConf.Config.Format)
if !contains(b.config.Formats, extConfig.Config.Format) {
builderLog.Warnf("This builder is not configured to build for format %s, ignore event", extConfig.Config.Format)
return
}

builderLog.Infof("Building for %s/%s (format: %s)", implantConf.Config.GOOS, implantConf.Config.GOARCH, implantConf.Config.Format)
builderLog.Infof("Building for %s/%s (format: %s)", extConfig.Config.GOOS, extConfig.Config.GOARCH, extConfig.Config.Format)
if extConfig.Config.TemplateName == "" {
extConfig.Config.TemplateName = generate.SliverTemplateName
}
if extConfig == nil {
return
}

extModel := models.ImplantConfig{}.FromProtobuf(extConfig.Config)

var fPath string
switch extConfig.Config.Format {
case clientpb.OutputFormat_SERVICE:
fallthrough
case clientpb.OutputFormat_EXECUTABLE:
fPath, err = generate.SliverExecutable(extConfig.Config.Name, extModel, false)
case clientpb.OutputFormat_SHARED_LIB:
fPath, err = generate.SliverSharedLibrary(extConfig.Config.Name, extModel, false)
case clientpb.OutputFormat_SHELLCODE:
fPath, err = generate.SliverShellcode(extConfig.Config.Name, extModel, false)
default:
builderLog.Errorf("invalid output format: %s", extConfig.Config.Format)
return
}
if err != nil {
builderLog.Errorf("Failed to generate sliver: %s", err)
return
}
data, err := os.ReadFile(fPath)
if err != nil {
builderLog.Errorf("Failed to read generated sliver: %s", err)
return
}

fileName := extConfig.Config.Name
if extConfig.Config.GOOS == "windows" {
fileName += ".exe"
}

_, err = b.rpc.GenerateExternalSaveBuild(context.Background(), &clientpb.ExternalImplantBinary{
Name: extConfig.Config.Name,
ImplantConfigID: extConfig.Config.ID,
File: &commonpb.File{
Name: extConfig.Config.Name,
Data: data,
},
})
if err != nil {
builderLog.Errorf("Failed to save build: %s", err)
return
}
}

func contains[T comparable](elems []T, v T) bool {
Expand Down
17 changes: 17 additions & 0 deletions server/cli/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

clientAssets "github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/client/transport"
"github.com/bishopfox/sliver/client/version"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/server/builder"
"github.com/bishopfox/sliver/server/log"
Expand All @@ -40,6 +41,16 @@ var builderCmd = &cobra.Command{
Short: "Start the process as an external builder",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {

quiet, err := cmd.Flags().GetBool(quietFlagStr)
if err != nil {
builderLog.Fatalf("Failed to parse --%s flag %s\n", quietFlagStr, err)
}
if !quiet {
log.RootLogger.AddHook(log.NewStdoutHook(log.RootLoggerName))
}
builderLog.Infof("Starting Sliver external builder - %s", version.FullVersion())

defer func() {
if r := recover(); r != nil {
builderLog.Printf("panic:\n%s", debug.Stack())
Expand Down Expand Up @@ -79,10 +90,12 @@ func parseBuilderConfigFlags(cmd *cobra.Command) builder.Config {
if err != nil {
builderLog.Fatalf("Failed to parse --%s flag %s\n", goosFlagStr, err)
}
builderLog.Debugf("GOOS enabled: %v", conf.GOOSs)
conf.GOARCHs, err = cmd.Flags().GetStringSlice(goarchFlagStr)
if err != nil {
builderLog.Fatalf("Failed to parse --%s flag %s\n", goarchFlagStr, err)
}
builderLog.Debugf("GOARCH enabled: %v", conf.GOARCHs)
rawFormats, err := cmd.Flags().GetStringSlice(formatFlagStr)
if err != nil {
builderLog.Fatalf("Failed to parse --%s flag %s\n", formatFlagStr, err)
Expand All @@ -91,12 +104,16 @@ func parseBuilderConfigFlags(cmd *cobra.Command) builder.Config {
for _, rawFormat := range rawFormats {
switch strings.ToLower(rawFormat) {
case "exe", "executable", "pe":
builderLog.Debugf("Executable format enabled (%d)", clientpb.OutputFormat_EXECUTABLE)
conf.Formats = append(conf.Formats, clientpb.OutputFormat_EXECUTABLE)
case "dll", "so", "shared", "dylib", "lib", "library":
builderLog.Debugf("Library format enabled (%d)", clientpb.OutputFormat_SHARED_LIB)
conf.Formats = append(conf.Formats, clientpb.OutputFormat_SHARED_LIB)
case "service":
builderLog.Debugf("Service format enabled (%d)", clientpb.OutputFormat_SERVICE)
conf.Formats = append(conf.Formats, clientpb.OutputFormat_SERVICE)
case "bin", "shellcode":
builderLog.Debugf("Shellcode format enabled (%d)", clientpb.OutputFormat_SHELLCODE)
conf.Formats = append(conf.Formats, clientpb.OutputFormat_SHELLCODE)
}
}
Expand Down
2 changes: 2 additions & 0 deletions server/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const (
goarchFlagStr = "arch"
operatorConfigFlagStr = "config"
formatFlagStr = "format"
quietFlagStr = "quiet"

// console log file name
logFileName = "console.log"
Expand Down Expand Up @@ -106,6 +107,7 @@ func init() {
builderCmd.Flags().StringSliceP(goarchFlagStr, "a", []string{runtime.GOARCH}, "builder supported arch targets")
builderCmd.Flags().StringSliceP(formatFlagStr, "f", []string{"executable"}, "builder supported formats")
builderCmd.Flags().StringP(operatorConfigFlagStr, "c", "", "operator config file path")
builderCmd.Flags().BoolP(quietFlagStr, "q", false, "do not write any content to stdout")
rootCmd.AddCommand(builderCmd)

// Version
Expand Down
44 changes: 44 additions & 0 deletions server/db/models/implant.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,50 @@ func (ic *ImplantConfig) BeforeCreate(tx *gorm.DB) (err error) {
return nil
}

func (ic ImplantConfig) FromProtobuf(pbConfig *clientpb.ImplantConfig) *ImplantConfig {
return &ImplantConfig{
ID: uuid.FromStringOrNil(pbConfig.ID),
IsBeacon: pbConfig.IsBeacon,
BeaconInterval: pbConfig.BeaconInterval,
BeaconJitter: pbConfig.BeaconJitter,

GOOS: pbConfig.GOOS,
GOARCH: pbConfig.GOARCH,

MtlsCACert: pbConfig.MtlsCACert,
MtlsCert: pbConfig.MtlsCert,
MtlsKey: pbConfig.MtlsKey,

Debug: pbConfig.Debug,
Evasion: pbConfig.Evasion,
ObfuscateSymbols: pbConfig.ObfuscateSymbols,
TemplateName: pbConfig.TemplateName,

ReconnectInterval: pbConfig.ReconnectInterval,
MaxConnectionErrors: pbConfig.MaxConnectionErrors,
ConnectionStrategy: pbConfig.ConnectionStrategy,

LimitDatetime: pbConfig.LimitDatetime,
LimitDomainJoined: pbConfig.LimitDomainJoined,
LimitHostname: pbConfig.LimitHostname,
LimitUsername: pbConfig.LimitUsername,
LimitFileExists: pbConfig.LimitFileExists,
LimitLocale: pbConfig.LimitLocale,

IsSharedLib: pbConfig.IsSharedLib,
IsService: pbConfig.IsService,
IsShellcode: pbConfig.IsShellcode,
Format: pbConfig.Format,
WGImplantPrivKey: pbConfig.WGImplantPrivKey,
WGServerPubKey: pbConfig.WGServerPubKey,
WGPeerTunIP: pbConfig.WGPeerTunIP,
WGKeyExchangePort: pbConfig.WGKeyExchangePort,
WGTcpCommsPort: pbConfig.WGTcpCommsPort,

FileName: ic.FileName,
}
}

// ToProtobuf - Convert ImplantConfig to protobuf equiv
func (ic *ImplantConfig) ToProtobuf() *clientpb.ImplantConfig {
config := &clientpb.ImplantConfig{
Expand Down
30 changes: 18 additions & 12 deletions server/generate/binaries.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func GetSliversDir() string {
// -----------------------

// SliverShellcode - Generates a sliver shellcode using Donut
func SliverShellcode(name string, config *models.ImplantConfig) (string, error) {
func SliverShellcode(name string, config *models.ImplantConfig, save bool) (string, error) {
if config.GOOS != "windows" {
return "", fmt.Errorf("shellcode format is currently only supported on Windows")
}
Expand Down Expand Up @@ -313,16 +313,18 @@ func SliverShellcode(name string, config *models.ImplantConfig) (string, error)
}
config.Format = clientpb.OutputFormat_SHELLCODE
// Save to database
saveBuildErr := ImplantBuildSave(name, config, dest)
if saveBuildErr != nil {
buildLog.Errorf("Failed to save build: %s", saveBuildErr)
if save {
saveBuildErr := ImplantBuildSave(name, config, dest)
if saveBuildErr != nil {
buildLog.Errorf("Failed to save build: %s", saveBuildErr)
}
}
return dest, err

}

// SliverSharedLibrary - Generates a sliver shared library (DLL/dylib/so) binary
func SliverSharedLibrary(name string, config *models.ImplantConfig) (string, error) {
func SliverSharedLibrary(name string, config *models.ImplantConfig, save bool) (string, error) {
// Compile go code
var cc string
var cxx string
Expand Down Expand Up @@ -390,15 +392,17 @@ func SliverSharedLibrary(name string, config *models.ImplantConfig) (string, err
}
config.FileName = filepath.Base(dest)

err = ImplantBuildSave(name, config, dest)
if err != nil {
buildLog.Errorf("Failed to save build: %s", err)
if save {
err = ImplantBuildSave(name, config, dest)
if err != nil {
buildLog.Errorf("Failed to save build: %s", err)
}
}
return dest, err
}

// SliverExecutable - Generates a sliver executable binary
func SliverExecutable(name string, config *models.ImplantConfig) (string, error) {
func SliverExecutable(name string, config *models.ImplantConfig, save bool) (string, error) {
// Compile go code
appDir := assets.GetRootAppDir()
cgo := "0"
Expand Down Expand Up @@ -451,9 +455,11 @@ func SliverExecutable(name string, config *models.ImplantConfig) (string, error)
return "", err
}
config.FileName = filepath.Base(dest)
err = ImplantBuildSave(name, config, dest)
if err != nil {
buildLog.Errorf("Failed to save build: %s", err)
if save {
err = ImplantBuildSave(name, config, dest)
if err != nil {
buildLog.Errorf("Failed to save build: %s", err)
}
}
return dest, err
}
Expand Down
20 changes: 10 additions & 10 deletions server/generate/binaries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func mtlsExe(t *testing.T, goos string, goarch string, beacon bool, debug bool)
IsBeacon: beacon,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("mtls_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("mtls_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand All @@ -193,7 +193,7 @@ func dnsExe(t *testing.T, goos string, goarch string, beacon bool, debug bool) {
IsBeacon: beacon,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("dns_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("dns_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand All @@ -217,7 +217,7 @@ func httpExe(t *testing.T, goos string, goarch string, beacon bool, debug bool)
IsBeacon: beacon,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("http_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("http_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand Down Expand Up @@ -245,7 +245,7 @@ func multiExe(t *testing.T, goos string, goarch string, beacon bool, debug bool)
IsBeacon: beacon,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("multi_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("multi_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand All @@ -272,7 +272,7 @@ func multiWindowsService(t *testing.T, goos string, goarch string, beacon bool,
IsBeacon: beacon,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("service_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("service_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand All @@ -296,7 +296,7 @@ func tcpPivotExe(t *testing.T, goos string, goarch string, debug bool) {
ObfuscateSymbols: false,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("tcpPivot_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("tcpPivot_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand All @@ -319,7 +319,7 @@ func namedPipeExe(t *testing.T, goos string, goarch string, debug bool) {
ObfuscateSymbols: false,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("namedpipe_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("namedpipe_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand Down Expand Up @@ -349,7 +349,7 @@ func wireguardExe(t *testing.T, goos string, goarch string, beacon bool, debug b
}
nonce++
certs.SetupWGKeys()
_, err := SliverExecutable(fmt.Sprintf("wireguard_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("wireguard_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand Down Expand Up @@ -381,7 +381,7 @@ func multiLibrary(t *testing.T, goos string, goarch string, debug bool) {
WGTcpCommsPort: 5678,
}
nonce++
_, err := SliverSharedLibrary(fmt.Sprintf("multilibrary_test%d", nonce), config)
_, err := SliverSharedLibrary(fmt.Sprintf("multilibrary_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand All @@ -404,7 +404,7 @@ func symbolObfuscation(t *testing.T, goos string, goarch string) {
ObfuscateSymbols: true,
}
nonce++
_, err := SliverExecutable(fmt.Sprintf("symbol_test%d", nonce), config)
_, err := SliverExecutable(fmt.Sprintf("symbol_test%d", nonce), config, true)
if err != nil {
t.Fatalf("%v", err)
}
Expand Down
Loading

0 comments on commit 0fab805

Please sign in to comment.