Skip to content

Commit

Permalink
added profile randomization during implant generation
Browse files Browse the repository at this point in the history
  • Loading branch information
TimBF committed May 30, 2023
1 parent 429c520 commit c03975d
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 132 deletions.
131 changes: 0 additions & 131 deletions server/configs/http-c2.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ package configs
import (
"errors"
"fmt"
insecureRand "math/rand"
"regexp"
"strings"

Expand All @@ -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"`
Expand Down Expand Up @@ -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)
Expand Down
160 changes: 160 additions & 0 deletions server/db/models/http-c2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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;"`
Expand Down Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion server/generate/binaries.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit c03975d

Please sign in to comment.