diff --git a/server/configs/http-c2.go b/server/configs/http-c2.go index b10e7ddd68..6de0116b60 100644 --- a/server/configs/http-c2.go +++ b/server/configs/http-c2.go @@ -21,7 +21,6 @@ package configs import ( "errors" "fmt" - insecureRand "math/rand" "regexp" "strings" @@ -40,84 +39,6 @@ type HTTPC2Config struct { ServerConfig *HTTPC2ServerConfig `json:"server_config"` } -// RandomImplantConfig - Randomly generate a new implant config from the parent config, -// this is the primary configuration used by the implant generation. -func (h *HTTPC2Config) RandomImplantConfig() *HTTPC2ImplantConfig { - return &HTTPC2ImplantConfig{ - - NonceQueryArgChars: h.ImplantConfig.NonceQueryArgChars, - URLParameters: h.ImplantConfig.URLParameters, - Headers: h.ImplantConfig.Headers, - - PollFileExt: h.ImplantConfig.PollFileExt, - PollFiles: h.ImplantConfig.RandomPollFiles(), - PollPaths: h.ImplantConfig.RandomPollPaths(), - - StartSessionFileExt: h.ImplantConfig.StartSessionFileExt, - SessionFileExt: h.ImplantConfig.SessionFileExt, - SessionFiles: h.ImplantConfig.RandomSessionFiles(), - SessionPaths: h.ImplantConfig.RandomSessionPaths(), - - CloseFileExt: h.ImplantConfig.CloseFileExt, - CloseFiles: h.ImplantConfig.RandomCloseFiles(), - ClosePaths: h.ImplantConfig.RandomClosePaths(), - } -} - -// 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 %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", h.MacOSVer(), 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 { - chromeVer := h.ImplantConfig.ChromeBaseVersion - if chromeVer == 0 { - chromeVer = DefaultChromeBaseVer - } - return fmt.Sprintf("%d.0.%d.%d", chromeVer+insecureRand.Intn(3), 1000+insecureRand.Intn(8999), insecureRand.Intn(999)) -} - -func (h *HTTPC2Config) MacOSVer() string { - macosVer := h.ImplantConfig.MacOSVersion - if macosVer == "" { - macosVer = DefaultMacOSVer - } - return macosVer -} - // HTTPC2ServerConfig - Server configuration options type HTTPC2ServerConfig struct { RandomVersionHeaders bool `json:"random_version_headers"` @@ -177,58 +98,6 @@ type HTTPC2ImplantConfig struct { ClosePaths []string `json:"close_paths"` } -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 -} - // CheckHTTPC2ConfigErrors - Get the current HTTP C2 config func CheckHTTPC2ConfigErrors(config *clientpb.HTTPC2Config) error { err := checkHTTPC2Config(config) diff --git a/server/db/models/http-c2.go b/server/db/models/http-c2.go index bc98684cd5..84d3cc2097 100644 --- a/server/db/models/http-c2.go +++ b/server/db/models/http-c2.go @@ -19,13 +19,21 @@ package models */ import ( + "fmt" "time" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/gofrs/uuid" + insecureRand "math/rand" + "gorm.io/gorm" ) +const ( + DefaultChromeBaseVer = 106 + DefaultMacOSVer = "10_15_7" +) + // HttpC2Config - type HttpC2Config struct { ID uuid.UUID `gorm:"primaryKey;->;<-:create;type:uuid;"` @@ -351,3 +359,155 @@ func HTTPC2ConfigFromProtobuf(pbHttpC2Config *clientpb.HTTPC2Config) *HttpC2Conf return cfg } + +// RandomImplantConfig - Randomly generate a new implant config from the parent config, +// this is the primary configuration used by the implant generation. +func RandomizeImplantConfig(h *clientpb.HTTPC2ImplantConfig, goos string, goarch string) *clientpb.HTTPC2ImplantConfig { + return &clientpb.HTTPC2ImplantConfig{ + + NonceQueryArgChars: h.NonceQueryArgChars, + ExtraURLParameters: h.ExtraURLParameters, + Headers: h.Headers, + + PollFileExtension: h.PollFileExtension, + StartSessionFileExtension: h.StartSessionFileExtension, + SessionFileExtension: h.SessionFileExtension, + CloseFileExtension: h.CloseFileExtension, + PathSegments: RandomPathSegments(h), + UserAgent: GenerateUserAgent(goos, goarch, h.UserAgent, h.ChromeBaseVersion, h.MacOSVersion), + } +} + +// GenerateUserAgent - Generate a user-agent depending on OS/Arch +func GenerateUserAgent(goos string, goarch string, userAgent string, baseVer int32, macOsVer string) string { + return generateChromeUserAgent(goos, goarch, userAgent, baseVer, macOsVer) +} + +func generateChromeUserAgent(goos string, goarch string, userAgent string, baseVer int32, macOsVer string) string { + if 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", ChromeVer(baseVer)) + } + + 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", ChromeVer(baseVer)) + } + + 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 %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", MacOSVer(macOsVer), ChromeVer(baseVer)) + } + + } + } else { + return 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", ChromeVer(baseVer)) +} + +// ChromeVer - Generate a random Chrome user-agent +func ChromeVer(baseVer int32) string { + chromeVer := baseVer + if chromeVer == 0 { + chromeVer = DefaultChromeBaseVer + } + return fmt.Sprintf("%d.0.%d.%d", baseVer+int32(insecureRand.Intn(3)), 1000+int32(insecureRand.Intn(8999)), int32(insecureRand.Intn(999))) +} + +func MacOSVer(MacOSVersion string) string { + macosVer := MacOSVersion + if macosVer == "" { + macosVer = DefaultMacOSVer + } + return macosVer +} + +func RandomPathSegments(h *clientpb.HTTPC2ImplantConfig) []*clientpb.HTTPC2PathSegment { + + var ( + sessionPaths []*clientpb.HTTPC2PathSegment + closePaths []*clientpb.HTTPC2PathSegment + pollPaths []*clientpb.HTTPC2PathSegment + sessionFiles []*clientpb.HTTPC2PathSegment + closeFiles []*clientpb.HTTPC2PathSegment + pollFiles []*clientpb.HTTPC2PathSegment + ) + for _, pathSegment := range h.PathSegments { + switch pathSegment.SegmentType { + case 0: + if pathSegment.IsFile { + pollFiles = append(pollFiles, pathSegment) + } else { + pollPaths = append(pollPaths, pathSegment) + } + case 1: + if pathSegment.IsFile { + sessionFiles = append(sessionFiles, pathSegment) + } else { + sessionPaths = append(sessionPaths, pathSegment) + } + case 2: + if pathSegment.IsFile { + closeFiles = append(closeFiles, pathSegment) + } else { + closePaths = append(closePaths, pathSegment) + } + default: + continue + } + } + + sessionPaths = RandomPaths(sessionPaths, h.MinPaths, h.MaxPaths) + pollPaths = RandomPaths(pollPaths, h.MinPaths, h.MaxPaths) + closePaths = RandomPaths(closePaths, h.MinPaths, h.MaxPaths) + + sessionFiles = RandomFiles(sessionFiles, h.MinFiles, h.MaxFiles) + closeFiles = RandomFiles(closeFiles, h.MinFiles, h.MaxFiles) + pollFiles = RandomFiles(pollFiles, h.MinFiles, h.MaxFiles) + + var res []*clientpb.HTTPC2PathSegment + res = append(res, sessionPaths...) + res = append(res, closePaths...) + res = append(res, pollPaths...) + res = append(res, sessionFiles...) + res = append(res, closeFiles...) + res = append(res, pollFiles...) + return res +} + +func RandomFiles(httpC2PathSegments []*clientpb.HTTPC2PathSegment, minFiles int32, maxFiles int32) []*clientpb.HTTPC2PathSegment { + if minFiles < 1 { + minFiles = 1 + } + return randomSample(httpC2PathSegments, minFiles, maxFiles) +} + +func RandomPaths(httpC2PathSegments []*clientpb.HTTPC2PathSegment, minPaths int32, maxPaths int32) []*clientpb.HTTPC2PathSegment { + return randomSample(httpC2PathSegments, minPaths, maxPaths) +} + +func randomSample(values []*clientpb.HTTPC2PathSegment, min int32, max int32) []*clientpb.HTTPC2PathSegment { + count := int32(insecureRand.Intn(len(values))) + if count < min { + count = min + } + if max < count { + count = max + } + var sample []*clientpb.HTTPC2PathSegment + for i := 0; int32(len(sample)) < count; i++ { + index := (count + int32(i)) % int32(len(values)) + sample = append(sample, values[index]) + } + return sample +} diff --git a/server/generate/binaries.go b/server/generate/binaries.go index 108f5b01f1..458973f700 100644 --- a/server/generate/binaries.go +++ b/server/generate/binaries.go @@ -355,7 +355,7 @@ func renderSliverGoCode(name string, otpSecret string, config *clientpb.ImplantC } buildLog.Debugf("Generating new sliver binary '%s'", name) - + pbC2Implant = models.RandomizeImplantConfig(pbC2Implant, config.GOOS, config.GOARCH) sliversDir := GetSliversDir() // ~/.sliver/slivers projectGoPathDir := filepath.Join(sliversDir, config.GOOS, config.GOARCH, filepath.Base(name)) if _, err := os.Stat(projectGoPathDir); os.IsNotExist(err) {