Compare commits

..
Author SHA1 Message Date
Joe ee2a72ff5f Merge branch 'master' into fix/kill 2023-11-16 07:30:29 -08:00
Joe 86507dc5c5 Merge pull request #1459 from BishopFox/fix_external_build
adapt external builds to support http c2 configuration and new build …
2023-11-16 07:29:36 -08:00
Tim Makram Ghatas d2b43ebb9f adapt external builds to support http c2 configuration and new build system 2023-11-15 23:46:28 +01:00
Joe 3a0ce8b890 Merge pull request #1402 from BishopFox/cc07dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2023-11-14 11:51:21 -08:00
Joe 623579affb Merge branch 'master' into cc07dependabot/github_actions/actions/checkout-4 2023-11-14 11:50:18 -08:00
Joe 632eacf3fd Merge pull request #1457 from BishopFox/allow_selective_staging
Allow selective staging
2023-11-14 11:49:55 -08:00
Joe 6918743f03 Merge branch 'master' into allow_selective_staging 2023-11-14 07:55:35 -08:00
Joe 5d333af798 Merge pull request #1456 from BishopFox/fix_profile_update
allow profile updates
2023-11-14 07:55:22 -08:00
Tim Makram Ghatas 68cddc1de2 Merge branch 'master' into allow_selective_staging 2023-11-13 23:54:47 +01:00
Tim Makram Ghatas 8a04d8d882 removed unused http staging code and http stage flag. Added a new subcommand to implants, stage that allows to select which builds to exposed externally. ImplantBuilds now have a stage field marking whether they are available externally or not. 2023-11-13 23:53:05 +01:00
Joe 1d9d5cf85a Merge branch 'master' into cc07dependabot/github_actions/actions/checkout-4 2023-09-20 20:34:14 -07:00
Ronan Kervella b0c3549b08 Merge branch 'master' into fix/kill 2023-09-18 12:51:41 -07:00
dependabot[bot] b044a4584b Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-09-11 03:13:23 +00:00
Ronan Kervella fb5bd84811 Merge branch 'master' into fix/kill 2023-08-08 11:14:23 -07:00
rkervella ae8444fc75 fix kill for sessions 2023-08-08 09:28:52 -07:00
28 changed files with 3995 additions and 3715 deletions

No files matched your search

+2 -2
View File
@@ -22,7 +22,7 @@ jobs:
git build-essential zlib1g zlib1g-dev wget zip unzip
- name: Check Out Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -48,7 +48,7 @@ jobs:
go-version: "^1.21"
- name: Check Out Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
+2 -2
View File
@@ -22,7 +22,7 @@ jobs:
go-version: "^1.21"
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head of the pull request.
@@ -36,7 +36,7 @@ jobs:
git build-essential zlib1g zlib1g-dev wget zip unzip
- name: Check Out Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
+3 -3
View File
@@ -27,7 +27,7 @@ jobs:
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
- name: Check Out Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -58,7 +58,7 @@ jobs:
run: brew install mingw-w64
- name: Check Out Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
@@ -86,7 +86,7 @@ jobs:
go-version: "^1.21"
- name: Check Out Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Git Fetch Tags
run: git fetch --prune --unshallow --tags -f
+3 -3
View File
@@ -850,13 +850,13 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
if len(parts) != 2 {
continue
}
if parts[0] == externalImplantConfig.Config.ID {
if parts[0] == externalImplantConfig.Build.ID {
con.RemoveEventListener(listenerID)
return nil, fmt.Errorf("external build failed: %s", parts[1])
}
case consts.AcknowledgeBuildEvent:
if string(event.Data) == externalImplantConfig.Config.ID {
if string(event.Data) == externalImplantConfig.Build.ID {
msgF = "External build acknowledged by builder (template: %s) ... %s"
}
@@ -865,7 +865,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con
if len(parts) != 2 {
continue
}
if parts[0] == externalImplantConfig.Config.ID {
if parts[0] == externalImplantConfig.Build.ID {
con.RemoveEventListener(listenerID)
name = parts[1]
waiting = false
+39
View File
@@ -0,0 +1,39 @@
package generate
import (
"context"
"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
)
// ImplantsStageCmd - Serve a previously generated build
func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) {
builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{})
if err != nil {
con.PrintErrorf("Unable to load implant builds '%s'\n", err)
return
}
options := []string{}
for name, _ := range builds.Configs {
options = append(options, name)
}
prompt := &survey.MultiSelect{
Message: "Select sessions and beacons to expose:",
Options: options,
}
selected := []string{}
survey.AskOne(prompt, &selected)
_, err = con.Rpc.StageImplantBuild(context.Background(), &clientpb.ImplantStageReq{Build: selected})
if err != nil {
con.PrintErrorf("Failed to serve implant %s\n", err)
return
}
}
+2
View File
@@ -73,6 +73,7 @@ func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilt
"Debug",
"C2 Config",
"ID",
"Stage",
})
tw.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Asc},
@@ -126,6 +127,7 @@ func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilt
fmt.Sprintf("%v", config.Debug),
fmt.Sprintf(config.HTTPC2ConfigName),
fmt.Sprintf("%v", builds.ResourceIDs[sliverName].Value),
fmt.Sprintf("%v", builds.Staged[sliverName]),
})
}
-2
View File
@@ -37,7 +37,6 @@ func HTTPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
pollTimeout, _ := cmd.Flags().GetString("long-poll-timeout")
pollJitter, _ := cmd.Flags().GetString("long-poll-jitter")
website, _ := cmd.Flags().GetString("website")
staging, _ := cmd.Flags().GetBool("staging")
longPollTimeout, err := time.ParseDuration(pollTimeout)
if err != nil {
@@ -60,7 +59,6 @@ func HTTPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
EnforceOTP: !disableOTP,
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
Staging: staging,
})
if err != nil {
con.PrintErrorf("%s\n", err)
-2
View File
@@ -42,7 +42,6 @@ func HTTPSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
website, _ := cmd.Flags().GetString("website")
letsEncrypt, _ := cmd.Flags().GetBool("lets-encrypt")
disableRandomize, _ := cmd.Flags().GetBool("disable-randomized-jarm")
staging, _ := cmd.Flags().GetBool("staging")
longPollTimeout, err := time.ParseDuration(pollTimeout)
if err != nil {
@@ -76,7 +75,6 @@ func HTTPSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args
LongPollTimeout: int64(longPollTimeout),
LongPollJitter: int64(longPollJitter),
RandomizeJARM: !disableRandomize,
Staging: staging,
})
con.Println()
if err != nil {
+10
View File
@@ -1094,6 +1094,16 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.
carapace.Gen(implantsRmCmd).PositionalCompletion(generate.ImplantBuildNameCompleter(con))
implantBuildsCmd.AddCommand(implantsRmCmd)
implantStageCmd := &cobra.Command{
Use: consts.StageStr,
Short: "Serve a previously generated implant",
Long: help.GetHelpFor([]string{consts.ImplantBuildsStr, consts.StageStr}),
Run: func(cmd *cobra.Command, args []string) {
generate.ImplantsStageCmd(cmd, con, args)
},
}
implantBuildsCmd.AddCommand(implantStageCmd)
canariesCmd := &cobra.Command{
Use: consts.CanariesStr,
Short: "List previously generated canaries",
+4
View File
@@ -240,6 +240,10 @@ func sessionStartup() {
if connection != nil {
err := sessionMainLoop(connection)
if err != nil {
if err == ErrTerminate {
connection.Cleanup()
return
}
connectionErrors++
if transports.GetMaxConnectionErrors() < connectionErrors {
return
File diff suppressed because it is too large. Load diff
+8 -2
View File
@@ -216,11 +216,12 @@ message TrafficEncoderTests {
message ExternalImplantConfig {
ImplantConfig Config = 1;
ImplantBuild Build = 2;
HTTPC2Config HTTPC2 = 3;
}
message ExternalImplantBinary {
string Name = 1;
string ImplantConfigID = 2;
string ImplantBuildID = 2;
commonpb.File File = 3;
}
@@ -228,6 +229,11 @@ message ExternalImplantBinary {
message ImplantBuilds {
map<string, ImplantConfig> Configs = 1;
map<string, ResourceID> ResourceIDs = 2;
map<string, bool> staged = 3;
}
message ImplantStageReq {
repeated string Build = 1;
}
message ImplantBuild {
@@ -254,6 +260,7 @@ message ImplantBuild {
string MtlsCACert = 17;
string MtlsCert = 18;
string MtlsKey = 19;
bool Stage = 20;
}
message CompilerTarget {
@@ -382,7 +389,6 @@ message HTTPListenerReq {
int64 LongPollTimeout = 11;
int64 LongPollJitter = 12;
bool RandomizeJARM = 13; // Only valid with Secure = true
bool Staging = 14;
}
// Named Pipes Messages for pivoting
+4 -3
View File
@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc-gen-go v1.31.0
// protoc v4.25.0
// source: commonpb/common.proto
@@ -131,8 +131,9 @@ func (x *Request) GetSessionID() string {
}
// Response - Common fields used in all gRPC responses. Note that the Err field
// only used when the implant needs to return an error to the server.
// Client<->Server comms should use normal gRPC error handling.
//
// only used when the implant needs to return an error to the server.
// Client<->Server comms should use normal gRPC error handling.
type Response struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
+4 -6
View File
@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc-gen-go v1.31.0
// protoc v4.25.0
// source: dnspb/dns.proto
@@ -87,12 +87,10 @@ func (DNSMessageType) EnumDescriptor() ([]byte, []int) {
return file_dnspb_dns_proto_rawDescGZIP(), []int{0}
}
// NOTE: DNS is very space sensitive so certain fields are re-purposed
// depending on the DNSMessageType as noted below:
//
//NOTE: DNS is very space sensitive so certain fields are re-purposed
//depending on the DNSMessageType as noted below:
//
//[Type TOTP]: ID field is used for the TOTP code
//
// [Type TOTP]: ID field is used for the TOTP code
type DNSMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
+1039 -1039
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -57,8 +57,6 @@ service SliverRPC {
// *** Stager Listener ***
rpc StartTCPStagerListener(clientpb.StagerListenerReq)
returns (clientpb.StagerListener);
rpc StartHTTPStagerListener(clientpb.StagerListenerReq)
returns (clientpb.StagerListener);
// *** Loot ***
rpc LootAdd(clientpb.Loot) returns (clientpb.Loot);
@@ -90,10 +88,12 @@ service SliverRPC {
returns (clientpb.ExternalImplantConfig);
rpc GenerateExternalSaveBuild(clientpb.ExternalImplantBinary)
returns (commonpb.Empty);
rpc GenerateExternalGetImplantConfig(clientpb.ImplantConfig)
rpc GenerateExternalGetBuildConfig(clientpb.ImplantBuild)
returns (clientpb.ExternalImplantConfig);
rpc GenerateStage(clientpb.GenerateStageReq)
returns (clientpb.Generate);
rpc StageImplantBuild(clientpb.ImplantStageReq)
returns (commonpb.Empty);
// *** HTTP C2 Profiles ***
rpc GetHTTPC2Profiles(commonpb.Empty) returns (clientpb.HTTPC2Configs);
File diff suppressed because it is too large. Load diff
+7 -4
View File
@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc-gen-go v1.31.0
// protoc v4.25.0
// source: sliverpb/sliver.proto
@@ -173,7 +173,8 @@ func (PeerFailureType) EnumDescriptor() ([]byte, []int) {
}
// Envelope - Used to encode implant<->server messages since we
// cannot use gRPC due to the various transports used.
//
// cannot use gRPC due to the various transports used.
type Envelope struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -746,7 +747,8 @@ func (x *CloseSession) GetRequest() *commonpb.Request {
}
// Ping - Not ICMP, just sends a rount trip message to an implant to
// see if it's still responding.
//
// see if it's still responding.
type Ping struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -3439,7 +3441,8 @@ func (x *CurrentTokenOwner) GetResponse() *commonpb.Response {
}
// InvokeGetSystemReq - Implant-side version of GetSystemReq, this message
// contains the .Data based on the client's req.Config
//
// contains the .Data based on the client's req.Config
type InvokeGetSystemReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
+23 -49
View File
@@ -31,12 +31,9 @@ import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/rpcpb"
"github.com/bishopfox/sliver/server/codenames"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/db/models"
"github.com/bishopfox/sliver/server/generate"
"github.com/bishopfox/sliver/server/log"
"github.com/bishopfox/sliver/util"
)
var (
@@ -113,16 +110,16 @@ func handleBuildEvent(externalBuilder *clientpb.Builder, event *clientpb.Event,
return
}
implantConfigID := parts[1]
builderLog.Infof("Build event for implant config id: %s", implantConfigID)
extConfig, err := rpc.GenerateExternalGetImplantConfig(context.Background(), &clientpb.ImplantConfig{
ID: implantConfigID,
implantBuildID := parts[1]
builderLog.Infof("Build event for implant build id: %s", implantBuildID)
extConfig, err := rpc.GenerateExternalGetBuildConfig(context.Background(), &clientpb.ImplantBuild{
ID: implantBuildID,
})
if err != nil {
builderLog.Errorf("Failed to get implant config: %s", err)
builderLog.Errorf("Failed to get build config: %s", err)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, err.Error())),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, err.Error())),
})
return
}
@@ -130,7 +127,7 @@ func handleBuildEvent(externalBuilder *clientpb.Builder, event *clientpb.Event,
builderLog.Errorf("nil extConfig")
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, "nil external config")),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, "nil external config")),
})
return
}
@@ -139,7 +136,7 @@ func handleBuildEvent(externalBuilder *clientpb.Builder, event *clientpb.Event,
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(
fmt.Sprintf("%s:%s", implantConfigID, fmt.Sprintf("unsupported target %s:%s/%s", extConfig.Config.Format, extConfig.Config.GOOS, extConfig.Config.GOARCH)),
fmt.Sprintf("%s:%s", implantBuildID, fmt.Sprintf("unsupported target %s:%s/%s", extConfig.Config.Format, extConfig.Config.GOOS, extConfig.Config.GOARCH)),
),
})
return
@@ -148,60 +145,37 @@ func handleBuildEvent(externalBuilder *clientpb.Builder, event *clientpb.Event,
builderLog.Warnf("Reject event, unsupported template '%s'", extConfig.Config.TemplateName)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, "Unsupported template")),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, "Unsupported template")),
})
return
}
name, _ := codenames.GetCodename()
err = util.AllowedName(name)
if err != nil {
builderLog.Errorf("Invalid implant name: %s", err)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, err.Error())),
})
return
}
extModel := models.ImplantConfigFromProtobuf(extConfig.Config)
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(extConfig.Config.HTTPC2ConfigName)
if err != nil {
builderLog.Errorf("Unable to load HTTP C2 Configuration: %s", err)
return
}
builderLog.Infof("Building %s for %s/%s (format: %s)", name, extConfig.Config.GOOS, extConfig.Config.GOARCH, extConfig.Config.Format)
builderLog.Infof("Building %s for %s/%s (format: %s)", extConfig.Build.Name, extConfig.Config.GOOS, extConfig.Config.GOARCH, extConfig.Config.Format)
builderLog.Infof(" [c2] mtls:%t wg:%t http/s:%t dns:%t", extModel.IncludeMTLS, extModel.IncludeWG, extModel.IncludeHTTP, extModel.IncludeDNS)
builderLog.Infof("[pivots] tcp:%t named-pipe:%t", extModel.IncludeTCP, extModel.IncludeNamePipe)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.AcknowledgeBuildEvent,
Data: []byte(implantConfigID),
Data: []byte(implantBuildID),
})
build, err := generate.GenerateConfig(name, extConfig.Config)
if err != nil {
builderLog.Errorf("Failed to generate config: %s", err)
return
}
var fPath string
switch extConfig.Config.Format {
case clientpb.OutputFormat_SERVICE:
fallthrough
case clientpb.OutputFormat_EXECUTABLE:
fPath, err = generate.SliverExecutable(name, build, extConfig.Config, httpC2Config.ImplantConfig)
fPath, err = generate.SliverExecutable(extConfig.Build.Name, extConfig.Build, extConfig.Config, extConfig.HTTPC2.ImplantConfig)
case clientpb.OutputFormat_SHARED_LIB:
fPath, err = generate.SliverSharedLibrary(name, build, extConfig.Config, httpC2Config.ImplantConfig)
fPath, err = generate.SliverSharedLibrary(extConfig.Build.Name, extConfig.Build, extConfig.Config, extConfig.HTTPC2.ImplantConfig)
case clientpb.OutputFormat_SHELLCODE:
fPath, err = generate.SliverShellcode(name, build, extConfig.Config, httpC2Config.ImplantConfig)
fPath, err = generate.SliverShellcode(extConfig.Build.Name, extConfig.Build, extConfig.Config, extConfig.HTTPC2.ImplantConfig)
default:
builderLog.Errorf("invalid output format: %s", extConfig.Config.Format)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, err.Error())),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, err.Error())),
})
return
}
@@ -209,7 +183,7 @@ func handleBuildEvent(externalBuilder *clientpb.Builder, event *clientpb.Event,
builderLog.Errorf("Failed to generate sliver: %s", err)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, err.Error())),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, err.Error())),
})
return
}
@@ -220,20 +194,20 @@ func handleBuildEvent(externalBuilder *clientpb.Builder, event *clientpb.Event,
builderLog.Errorf("Failed to read generated sliver: %s", err)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, err.Error())),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, err.Error())),
})
return
}
fileName := filepath.Base(name)
fileName := filepath.Base(extConfig.Build.Name)
if extConfig.Config.GOOS == "windows" {
fileName += ".exe"
}
builderLog.Infof("Uploading '%s' to server ...", name)
builderLog.Infof("Uploading '%s' to server ...", extConfig.Build.Name)
_, err = rpc.GenerateExternalSaveBuild(context.Background(), &clientpb.ExternalImplantBinary{
Name: name,
ImplantConfigID: extConfig.Config.ID,
Name: extConfig.Build.Name,
ImplantBuildID: extConfig.Build.ID,
File: &commonpb.File{
Name: fileName,
Data: data,
@@ -243,13 +217,13 @@ func handleBuildEvent(externalBuilder *clientpb.Builder, event *clientpb.Event,
builderLog.Errorf("Failed to save build: %s", err)
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildFailedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, err.Error())),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, err.Error())),
})
return
}
rpc.BuilderTrigger(context.Background(), &clientpb.Event{
EventType: consts.ExternalBuildCompletedEvent,
Data: []byte(fmt.Sprintf("%s:%s", implantConfigID, name)),
Data: []byte(fmt.Sprintf("%s:%s", implantBuildID, extConfig.Build.Name)),
})
builderLog.Infof("All done, built and saved %s", fileName)
}
+23 -32
View File
@@ -122,7 +122,6 @@ type SliverHTTPC2 struct {
HTTPServer *http.Server
ServerConf *clientpb.HTTPListenerReq // Server config (user args)
HTTPSessions *HTTPSessions
SliverStage []byte // Sliver shellcode to serve during staging process
Cleanup func()
c2Config []*clientpb.HTTPC2Config // C2 configs
@@ -347,17 +346,15 @@ func (s *SliverHTTPC2) router() *mux.Router {
s.ServerConf.LongPollJitter = int64(DefaultLongPollJitter)
}
if s.ServerConf.Staging {
// start stager handlers, extension are unique accross all profiles
for _, c2Config := range c2Configs.Configs {
// 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(
fmt.Sprintf("/{rpath:.*\\.%s[/]{0,1}.*$}", c2Config.ImplantConfig.StagerFileExtension),
s.stagerHandler,
).Methods(http.MethodGet)
}
// start stager handlers, extension are unique accross all profiles
for _, c2Config := range c2Configs.Configs {
// 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(
fmt.Sprintf("/{rpath:.*\\.%s[/]{0,1}.*$}", c2Config.ImplantConfig.StagerFileExtension),
s.stagerHandler,
).Methods(http.MethodGet)
}
router.HandleFunc("/{rpath:.*}", s.mainHandler).Methods(http.MethodGet, http.MethodPost)
@@ -760,13 +757,7 @@ func (s *SliverHTTPC2) closeHandler(resp http.ResponseWriter, req *http.Request)
func (s *SliverHTTPC2) stagerHandler(resp http.ResponseWriter, req *http.Request) {
nonce, _ := getNonceFromURL(req.URL)
httpLog.Debug("Stager request")
if len(s.SliverStage) != 0 {
httpLog.Infof("Received staging request from %s", getRemoteAddr(req))
s.noCacheHeader(resp)
resp.Write(s.SliverStage)
httpLog.Infof("Serving sliver shellcode (size %d) to %s", len(s.SliverStage), getRemoteAddr(req))
resp.WriteHeader(http.StatusOK)
} else if nonce != 0 {
if nonce != 0 {
resourceID, err := db.ResourceIDByValue(nonce)
if err != nil {
httpLog.Infof("No profile with id %#v", nonce)
@@ -774,21 +765,21 @@ func (s *SliverHTTPC2) stagerHandler(resp http.ResponseWriter, req *http.Request
return
}
build, _ := db.ImplantBuildByResourceID(resourceID.Value)
payload, err := generate.ImplantFileFromBuild(build)
if err != nil {
httpLog.Infof("Unable to retrieve Implant build %s", build)
s.defaultHandler(resp, req)
return
if build.Stage {
payload, err := generate.ImplantFileFromBuild(build)
if err != nil {
httpLog.Infof("Unable to retrieve Implant build %s", build)
s.defaultHandler(resp, req)
return
}
httpLog.Infof("Received staging request from %s", getRemoteAddr(req))
s.noCacheHeader(resp)
resp.Write(payload)
httpLog.Infof("Serving sliver shellcode (size %d) %s to %s", len(payload), resourceID.Name, getRemoteAddr(req))
resp.WriteHeader(http.StatusOK)
}
httpLog.Infof("Received staging request from %s", getRemoteAddr(req))
s.noCacheHeader(resp)
resp.Write(payload)
httpLog.Infof("Serving sliver shellcode (size %d) %s to %s", len(payload), resourceID.Name, getRemoteAddr(req))
resp.WriteHeader(http.StatusOK)
} else {
s.defaultHandler(resp, req)
}
s.defaultHandler(resp, req)
}
func (s *SliverHTTPC2) getHTTPSession(req *http.Request) *HTTPSession {
-58
View File
@@ -263,64 +263,6 @@ func StartTCPStagerListenerJob(host string, port uint16, profileName string, she
return job, nil
}
// StartHTTPStagerListenerJob - Start an HTTP(S) stager payload listener
func StartHTTPStagerListenerJob(req *clientpb.HTTPListenerReq, data []byte) (*core.Job, error) {
server, err := StartHTTPListener(req)
if err != nil {
return nil, err
}
name := constants.HttpStr
if req.Secure {
name = constants.HttpsStr
}
server.SliverStage = data
job := &core.Job{
ID: core.NextJobID(),
Name: name,
Description: fmt.Sprintf("Stager handler %s for domain %s", name, req.Domain),
Protocol: constants.TCPListenerStr,
Port: uint16(req.Port),
JobCtrl: make(chan bool),
}
core.Jobs.Add(job)
cleanup := func(err error) {
server.Cleanup()
core.Jobs.Remove(job)
core.EventBroker.Publish(core.Event{
Job: job,
EventType: consts.JobStoppedEvent,
Err: err,
})
}
once := &sync.Once{}
go func() {
var err error
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, req.Cert, req.Key)
}
} else {
err = server.HTTPServer.ListenAndServe()
}
if err != nil {
jobLog.Errorf("%s listener error %v", name, err)
once.Do(func() { cleanup(err) })
job.JobCtrl <- true // Cleanup other goroutine
}
}()
go func() {
<-job.JobCtrl
once.Do(func() { cleanup(nil) })
}()
return job, nil
}
// Fuck'in Go - https://stackoverflow.com/questions/30815244/golang-https-server-passing-certfile-and-kyefile-in-terms-of-byte-array
// basically the same as server.ListenAndServerTLS() but we can pass in byte slices instead of file paths
func listenAndServeTLS(srv *http.Server, certPEMBlock, keyPEMBlock []byte) error {
+51 -10
View File
@@ -115,6 +115,7 @@ func ImplantBuilds() (*clientpb.ImplantBuilds, error) {
pbBuilds := &clientpb.ImplantBuilds{
Configs: map[string]*clientpb.ImplantConfig{},
ResourceIDs: map[string]*clientpb.ResourceID{},
Staged: map[string]bool{},
}
for _, dbBuild := range builds {
config, err := ImplantConfigByID(dbBuild.ImplantConfigID.String())
@@ -128,6 +129,8 @@ func ImplantBuilds() (*clientpb.ImplantBuilds, error) {
return nil, err
}
pbBuilds.ResourceIDs[dbBuild.Name] = resource
pbBuilds.Staged[dbBuild.Name] = dbBuild.Stage
}
return pbBuilds, err
@@ -135,25 +138,50 @@ func ImplantBuilds() (*clientpb.ImplantBuilds, error) {
// SaveImplantBuild
func SaveImplantBuild(ib *clientpb.ImplantBuild) (*clientpb.ImplantBuild, error) {
implantBuild := models.ImplantBuildFromProtobuf(ib)
dbSession := Session()
err := dbSession.Create(&implantBuild).Error
if err != nil {
return nil, err
}
var implantBuild *models.ImplantBuild
if ib.ID != "" {
_, err := ImplantBuildByID(ib.ID)
if err != nil {
return nil, err
}
implantBuild = models.ImplantBuildFromProtobuf(ib)
err = dbSession.Save(implantBuild).Error
if err != nil {
return nil, err
}
} else {
implantBuild = models.ImplantBuildFromProtobuf(ib)
err := dbSession.Create(&implantBuild).Error
if err != nil {
return nil, err
}
}
return implantBuild.ToProtobuf(), nil
}
// SaveImplantConfig
func SaveImplantConfig(ic *clientpb.ImplantConfig) (*clientpb.ImplantConfig, error) {
implantConfig := models.ImplantConfigFromProtobuf(ic)
dbSession := Session()
err := dbSession.Create(&implantConfig).Error
if err != nil {
return nil, err
var implantConfig *models.ImplantConfig
if ic.ID != "" {
_, err := ImplantConfigByID(ic.ID)
if err != nil {
return nil, err
}
implantConfig = models.ImplantConfigFromProtobuf(ic)
err = dbSession.Save(implantConfig).Error
if err != nil {
return nil, err
}
} else {
implantConfig = models.ImplantConfigFromProtobuf(ic)
err := dbSession.Create(&implantConfig).Error
if err != nil {
return nil, err
}
}
return implantConfig.ToProtobuf(), nil
}
@@ -184,6 +212,19 @@ func ImplantBuildByResourceID(resourceID uint64) (*clientpb.ImplantBuild, error)
return build.ToProtobuf(), nil
}
// ImplantBuildByID - Fetch implant build from ID
func ImplantBuildByID(id string) (*clientpb.ImplantBuild, error) {
build := models.ImplantBuild{}
uuid, _ := uuid.FromString(id)
err := Session().Where(&models.ImplantBuild{
ID: uuid,
}).Find(&build).Error
if err != nil {
return nil, err
}
return build.ToProtobuf(), nil
}
// ImplantProfiles - Fetch a map of name<->profiles current in the database
func ImplantProfiles() ([]*clientpb.ImplantProfile, error) {
profiles := []*models.ImplantProfile{}
+4
View File
@@ -70,6 +70,8 @@ type ImplantBuild struct {
// WireGuard
WGImplantPrivKey string
WGServerPubKey string
Stage bool
}
// BeforeCreate - GORM hook
@@ -104,6 +106,7 @@ func (ib *ImplantBuild) ToProtobuf() *clientpb.ImplantBuild {
MtlsKey: ib.MtlsKey,
WGImplantPrivKey: ib.WGImplantPrivKey,
WGServerPubKey: ib.WGServerPubKey,
Stage: ib.Stage,
}
return &build
}
@@ -133,6 +136,7 @@ func ImplantBuildFromProtobuf(ib *clientpb.ImplantBuild) *ImplantBuild {
WGImplantPrivKey: ib.WGImplantPrivKey,
WGServerPubKey: ib.WGServerPubKey,
Stage: ib.Stage,
}
return &build
}
-3
View File
@@ -171,7 +171,6 @@ func (j *HTTPListener) ToProtobuf() *clientpb.HTTPListenerReq {
LongPollTimeout: int64(j.LongPollTimeout),
LongPollJitter: int64(j.LongPollJitter),
RandomizeJARM: j.RandomizeJarm,
Staging: j.Staging,
}
}
@@ -235,7 +234,6 @@ func ListenerJobFromProtobuf(pbListenerJob *clientpb.ListenerJob) *ListenerJob {
LongPollTimeout: pbListenerJob.HTTPConf.LongPollTimeout,
LongPollJitter: pbListenerJob.HTTPConf.LongPollJitter,
RandomizeJarm: pbListenerJob.HTTPConf.RandomizeJARM,
Staging: pbListenerJob.HTTPConf.Staging,
}
case constants.HttpsStr:
cfg.HttpListener = HTTPListener{
@@ -251,7 +249,6 @@ func ListenerJobFromProtobuf(pbListenerJob *clientpb.ListenerJob) *ListenerJob {
LongPollTimeout: pbListenerJob.HTTPConf.LongPollTimeout,
LongPollJitter: pbListenerJob.HTTPConf.LongPollJitter,
RandomizeJarm: pbListenerJob.HTTPConf.RandomizeJARM,
Staging: pbListenerJob.HTTPConf.Staging,
}
case constants.MtlsStr:
cfg.MtlsListener = MtlsListener{
+2
View File
@@ -749,6 +749,8 @@ func GenerateConfig(name string, implantConfig *clientpb.ImplantConfig) (*client
build.WGServerPubKey = serverPubKey
}
build.Stage = false
return &build, nil
}
+21 -1
View File
@@ -20,16 +20,36 @@ package generate
import (
"github.com/bishopfox/sliver/protobuf/clientpb"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/server/db/models"
)
// SliverExternal - Generates the cryptographic keys for the implant but compiles no code
func SliverExternal(name string, config *clientpb.ImplantConfig) (*clientpb.ExternalImplantConfig, error) {
config.IncludeMTLS = models.IsC2Enabled([]string{"mtls"}, config.C2)
config.IncludeWG = models.IsC2Enabled([]string{"wg"}, config.C2)
config.IncludeHTTP = models.IsC2Enabled([]string{"http", "https"}, config.C2)
config.IncludeDNS = models.IsC2Enabled([]string{"dns"}, config.C2)
config.IncludeNamePipe = models.IsC2Enabled([]string{"namedpipe"}, config.C2)
config.IncludeTCP = models.IsC2Enabled([]string{"tcppivot"}, config.C2)
build, err := GenerateConfig(name, config)
if err != nil {
return nil, err
}
config, err = db.SaveImplantConfig(config)
if err != nil {
return nil, err
}
build.ImplantConfigID = config.ID
implantBuild, err := db.SaveImplantBuild(build)
if err != nil {
return nil, err
}
return &clientpb.ExternalImplantConfig{
Config: config,
Build: build,
Build: implantBuild,
}, nil
}
+40 -32
View File
@@ -176,6 +176,22 @@ func (rpc *Server) ImplantBuilds(ctx context.Context, _ *commonpb.Empty) (*clien
return builds, nil
}
// StageImplantBuild - Serve a previously generated build
func (rpc *Server) StageImplantBuild(ctx context.Context, req *clientpb.ImplantStageReq) (*commonpb.Empty, error) {
err := db.Session().Model(&models.ImplantBuild{}).Where("Stage = ?", true).Update("Stage", false).Error
if err != nil {
return nil, err
}
for _, name := range req.Build {
err = db.Session().Model(&models.ImplantBuild{}).Where(&models.ImplantBuild{Name: name}).Update("Stage", true).Error
if err != nil {
return nil, err
}
}
return &commonpb.Empty{}, nil
}
// Canaries - List existing canaries
func (rpc *Server) Canaries(ctx context.Context, _ *commonpb.Empty) (*clientpb.Canaries, error) {
dbCanaries, err := db.ListCanaries()
@@ -339,7 +355,7 @@ func (rpc *Server) GenerateExternal(ctx context.Context, req *clientpb.ExternalG
core.EventBroker.Publish(core.Event{
EventType: consts.ExternalBuildEvent,
Data: []byte(fmt.Sprintf("%s:%s", req.BuilderName, config.ID)),
Data: []byte(fmt.Sprintf("%s:%s", req.BuilderName, externalConfig.Build.ID)),
})
return externalConfig, err
@@ -347,22 +363,14 @@ func (rpc *Server) GenerateExternal(ctx context.Context, req *clientpb.ExternalG
// GenerateExternalSaveBuild - Allows an external builder to save the build to the server
func (rpc *Server) GenerateExternalSaveBuild(ctx context.Context, req *clientpb.ExternalImplantBinary) (*commonpb.Empty, error) {
implantConfig, err := db.ImplantConfigWithC2sByID(req.ImplantConfigID)
implantBuild, err := db.ImplantBuildByID(req.ImplantBuildID)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid implant config id")
return nil, status.Error(codes.InvalidArgument, "invalid implant build id")
}
if implantConfig.TemplateName == "" {
return nil, status.Error(codes.InvalidArgument, "invalid payload name")
}
err = util.AllowedName(req.Name)
implantConfig, err := db.ImplantConfigWithC2sByID(implantBuild.ImplantConfigID)
if err != nil {
rpcLog.Errorf("Invalid build name: %s", err)
return nil, ErrInvalidName
}
_, err = db.ImplantBuildByName(req.Name)
if err == nil {
rpcLog.Errorf("Build '%s' already exists!", req.Name)
return nil, ErrBuildExists
return nil, status.Error(codes.Internal, "invalid implant config id")
}
tmpFile, err := os.CreateTemp(assets.GetRootAppDir(), "tmp-external-build-*")
@@ -377,20 +385,7 @@ func (rpc *Server) GenerateExternalSaveBuild(ctx context.Context, req *clientpb.
return nil, status.Error(codes.Internal, "Failed to write implant binary to temp file")
}
rpcLog.Infof("Saving external build '%s' from %s", req.Name, tmpFile.Name())
implantConfig, err = generate.ImplantConfigSave(implantConfig)
if err != nil {
rpcLog.Errorf("Failed to save implant config: %s", err)
return nil, err
}
build, err := generate.GenerateConfig(req.Name, implantConfig)
if err != nil {
rpcLog.Errorf("Failed to generate implant config: %s", err)
return nil, err
}
err = generate.ImplantBuildSave(build, implantConfig, tmpFile.Name())
err = generate.ImplantBuildSave(implantBuild, implantConfig, tmpFile.Name())
if err != nil {
rpcLog.Errorf("Failed to save external build: %s", err)
return nil, err
@@ -398,21 +393,34 @@ func (rpc *Server) GenerateExternalSaveBuild(ctx context.Context, req *clientpb.
core.EventBroker.Publish(core.Event{
EventType: consts.BuildCompletedEvent,
Data: []byte(req.Name),
Data: []byte(implantBuild.Name),
})
return &commonpb.Empty{}, nil
}
// GenerateExternalGetImplantConfig - Get an implant config for external builder
func (rpc *Server) GenerateExternalGetImplantConfig(ctx context.Context, req *clientpb.ImplantConfig) (*clientpb.ExternalImplantConfig, error) {
implantConfig, err := db.ImplantConfigWithC2sByID(req.ID)
func (rpc *Server) GenerateExternalGetBuildConfig(ctx context.Context, req *clientpb.ImplantBuild) (*clientpb.ExternalImplantConfig, error) {
build, err := db.ImplantBuildByID(req.ID)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid implant config id")
return nil, status.Error(codes.InvalidArgument, "invalid implant build id")
}
implantConfig, err := db.ImplantConfigWithC2sByID(build.ImplantConfigID)
if err != nil {
return nil, status.Error(codes.Internal, "invalid implant config id")
}
// retrieve http c2 implant config
httpC2Config, err := db.LoadHTTPC2ConfigByName(implantConfig.HTTPC2ConfigName)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("Unable to load HTTP C2 Configuration: %s", err))
}
return &clientpb.ExternalImplantConfig{
Config: implantConfig,
Build: build,
HTTPC2: httpC2Config,
}, nil
}
-30
View File
@@ -20,7 +20,6 @@ package rpc
import (
"context"
"fmt"
"net"
"github.com/bishopfox/sliver/protobuf/clientpb"
@@ -40,35 +39,6 @@ func (rpc *Server) StartTCPStagerListener(ctx context.Context, req *clientpb.Sta
return &clientpb.StagerListener{JobID: uint32(job.ID)}, nil
}
// StartHTTPStagerListener starts a HTTP(S) stager listener
func (rpc *Server) StartHTTPStagerListener(ctx context.Context, req *clientpb.StagerListenerReq) (*clientpb.StagerListener, error) {
host := req.GetHost()
if !checkInterface(req.GetHost()) {
host = "0.0.0.0"
}
conf := &clientpb.HTTPListenerReq{
Host: host,
Port: req.Port,
Domain: req.Host,
Secure: false,
}
if req.GetProtocol() == clientpb.StageProtocol_HTTPS {
conf.Secure = true
conf.Key = req.Key
conf.Cert = req.Cert
conf.ACME = req.ACME
}
job, err := c2.StartHTTPStagerListenerJob(conf, req.Data)
if err != nil {
return nil, err
}
if job == nil {
return nil, fmt.Errorf("job is nil")
}
return &clientpb.StagerListener{JobID: uint32(job.ID)}, err
}
// checkInterface verifies if an IP address
// is attached to an existing network interface
func checkInterface(a string) bool {