diff --git a/server/certs/acme.go b/server/certs/acme.go index a28c0c4551..45be1619f9 100644 --- a/server/certs/acme.go +++ b/server/certs/acme.go @@ -20,7 +20,7 @@ package certs import ( "os" - "path" + "path/filepath" "github.com/bishopfox/sliver/server/log" "golang.org/x/crypto/acme/autocert" @@ -37,7 +37,7 @@ var ( // GetACMEDir - Dir to store ACME certs func GetACMEDir() string { - acmePath := path.Join(getCertDir(), ACMEDirName) + acmePath := filepath.Join(getCertDir(), ACMEDirName) if _, err := os.Stat(acmePath); os.IsNotExist(err) { acmeLog.Infof("[mkdir] %s", acmePath) os.MkdirAll(acmePath, 0700) diff --git a/server/certs/ca.go b/server/certs/ca.go index 0300f58e40..7ebf12f321 100644 --- a/server/certs/ca.go +++ b/server/certs/ca.go @@ -25,7 +25,7 @@ import ( "fmt" "io/ioutil" "os" - "path" + "path/filepath" "github.com/bishopfox/sliver/server/assets" ) @@ -44,7 +44,7 @@ func SetupCAs() { func getCertDir() string { rootDir := assets.GetRootAppDir() - certDir := path.Join(rootDir, "certs") + certDir := filepath.Join(rootDir, "certs") if _, err := os.Stat(certDir); os.IsNotExist(err) { err := os.MkdirAll(certDir, 0700) if err != nil { @@ -57,7 +57,7 @@ func getCertDir() string { // GenerateCertificateAuthority - Creates a new CA cert for a given type func GenerateCertificateAuthority(caType string, commonName string) (*x509.Certificate, *ecdsa.PrivateKey) { storageDir := getCertDir() - certFilePath := path.Join(storageDir, fmt.Sprintf("%s-ca-cert.pem", caType)) + certFilePath := filepath.Join(storageDir, fmt.Sprintf("%s-ca-cert.pem", caType)) if _, err := os.Stat(certFilePath); os.IsNotExist(err) { certsLog.Infof("Generating certificate authority for '%s'", caType) cert, key := GenerateECCCertificate(caType, commonName, true, false) @@ -104,9 +104,9 @@ func GetCertificateAuthority(caType string) (*x509.Certificate, *ecdsa.PrivateKe // GetCertificateAuthorityPEM - Get PEM encoded CA cert/key func GetCertificateAuthorityPEM(caType string) ([]byte, []byte, error) { - caType = path.Base(caType) - caCertPath := path.Join(getCertDir(), fmt.Sprintf("%s-ca-cert.pem", caType)) - caKeyPath := path.Join(getCertDir(), fmt.Sprintf("%s-ca-key.pem", caType)) + caType = filepath.Base(caType) + caCertPath := filepath.Join(getCertDir(), fmt.Sprintf("%s-ca-cert.pem", caType)) + caKeyPath := filepath.Join(getCertDir(), fmt.Sprintf("%s-ca-key.pem", caType)) certPEM, err := ioutil.ReadFile(caCertPath) if err != nil { @@ -134,8 +134,8 @@ func SaveCertificateAuthority(caType string, cert []byte, key []byte) { // CAs get written to the filesystem since we control the names and makes them // easier to move around/backup - certFilePath := path.Join(storageDir, fmt.Sprintf("%s-ca-cert.pem", caType)) - keyFilePath := path.Join(storageDir, fmt.Sprintf("%s-ca-key.pem", caType)) + certFilePath := filepath.Join(storageDir, fmt.Sprintf("%s-ca-cert.pem", caType)) + keyFilePath := filepath.Join(storageDir, fmt.Sprintf("%s-ca-key.pem", caType)) err := ioutil.WriteFile(certFilePath, cert, 0600) if err != nil { diff --git a/server/codenames/codenames.go b/server/codenames/codenames.go index 49eab14709..b158414ef0 100644 --- a/server/codenames/codenames.go +++ b/server/codenames/codenames.go @@ -23,7 +23,7 @@ import ( "fmt" insecureRand "math/rand" "os" - "path" + "path/filepath" "strings" "github.com/bishopfox/sliver/server/assets" @@ -39,11 +39,11 @@ var ( }) ) -// readlines - Read lines of a text file into a slice -func readlines(fpath string) ([]string, error) { - file, err := os.Open(fpath) +// readLines - Read lines of a text file into a slice +func readLines(txtFilePath string) ([]string, error) { + file, err := os.Open(txtFilePath) if err != nil { - codenameLog.Errorf("Error opening %s: %v", fpath, err) + codenameLog.Errorf("Error opening %s: %v", txtFilePath, err) return nil, err } defer file.Close() @@ -63,26 +63,26 @@ func readlines(fpath string) ([]string, error) { } // getRandomWord - Get a random word from a file, not cryptographically secure -func getRandomWord(fpath string) (string, error) { +func getRandomWord(txtFilePath string) (string, error) { appDir := assets.GetRootAppDir() - words, err := readlines(path.Join(appDir, fpath)) + words, err := readLines(filepath.Join(appDir, txtFilePath)) if err != nil { return "", err } wordsLen := len(words) if wordsLen == 0 { - return "", fmt.Errorf("no words found in %s", fpath) + return "", fmt.Errorf("no words found in %s", txtFilePath) } word := words[insecureRand.Intn(wordsLen-1)] return strings.TrimSpace(word), nil } -// getRandomAdjective - Get a random noun, not cryptographically secure +// RandomAdjective - Get a random noun, not cryptographically secure func RandomAdjective() (string, error) { return getRandomWord("adjectives.txt") } -// getRandomNoun - Get a random noun, not cryptographically secure +// RandomNoun - Get a random noun, not cryptographically secure func RandomNoun() (string, error) { return getRandomWord("nouns.txt") } diff --git a/server/configs/http-c2.go b/server/configs/http-c2.go index 1229f1ab2d..4b90a264a9 100644 --- a/server/configs/http-c2.go +++ b/server/configs/http-c2.go @@ -35,7 +35,8 @@ import ( const ( httpC2ConfigFileName = "http-c2.json" - chromeBaseVer = 97 + DefaultChromeBaseVer = 100 + DefaultMacOSVer = "10_15_7" ) // HTTPC2Config - Parent config file struct for implant/server @@ -69,7 +70,7 @@ func (h *HTTPC2Config) generateChromeUserAgent(goos string, goarch string) strin 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 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", h.ChromeVer()) + 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()) } } @@ -83,7 +84,19 @@ func (h *HTTPC2Config) generateChromeUserAgent(goos string, goarch string) strin // ChromeVer - Generate a random Chrome user-agent func (h *HTTPC2Config) ChromeVer() string { - return fmt.Sprintf("%d.0.%d.%d", chromeBaseVer+insecureRand.Intn(3), 1000+insecureRand.Intn(8999), insecureRand.Intn(999)) + 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 } // RandomImplantConfig - Randomly generate a config @@ -126,7 +139,9 @@ type NameValueProbability struct { // .png = stop // .woff = sliver shellcode type HTTPC2ImplantConfig struct { - UserAgent string `json:"user_agent"` + UserAgent string `json:"user_agent"` + ChromeBaseVersion int `json:"chrome_base_version"` + MacOSVersion string `json:"macos_version"` URLParameters []NameValueProbability `json:"url_parameters"` Headers []NameValueProbability `json:"headers"` @@ -222,11 +237,13 @@ var ( }, }, ImplantConfig: &HTTPC2ImplantConfig{ - UserAgent: "", // Blank string is rendered as randomized platform user-agent - MaxFiles: 8, - MinFiles: 2, - MaxPaths: 8, - MinPaths: 2, + UserAgent: "", // Blank string is rendered as randomized platform user-agent + ChromeBaseVersion: DefaultChromeBaseVer, + MacOSVersion: DefaultMacOSVer, + MaxFiles: 8, + MinFiles: 2, + MaxPaths: 8, + MinPaths: 2, StagerFileExt: ".woff", diff --git a/server/core/socks.go b/server/core/socks.go index 1e049f5391..1cf015c18d 100644 --- a/server/core/socks.go +++ b/server/core/socks.go @@ -26,7 +26,7 @@ import ( ) var ( - // TunSocksTunnels - Interating with duplex SocksTunnels + // TunSocksTunnels - Interacting with duplex SocksTunnels SocksTunnels = tcpTunnel{ tunnels: map[uint64]*TcpTunnel{}, mutex: &sync.Mutex{}, diff --git a/server/core/tunnels.go b/server/core/tunnels.go index 36b76bb3ac..b1f8a1174c 100644 --- a/server/core/tunnels.go +++ b/server/core/tunnels.go @@ -31,7 +31,7 @@ import ( ) var ( - // Tunnels - Interating with duplex tunnels + // Tunnels - Interacting with duplex tunnels Tunnels = tunnels{ tunnels: map[uint64]*Tunnel{}, mutex: &sync.Mutex{}, @@ -125,10 +125,10 @@ func (t *tunnels) Create(sessionID string) *Tunnel { // will close it once there is no data for at least delayBeforeClose delay since last message // This is _necessary_ since we processing messages asynchronously // and if tunnelCloseHandler routine will fire before tunnelDataHandler routine we will lose some data -// (this is what happends for socks and portfwd) +// (this is what happens for socks and portfwd) // There is no another way around it, if we want to stick to async processing as we do now. // All additional changes requires changes on implants(like sequencing for close messages), -// and as there is a goal to keep compatability we don't do that at the moment. +// and as there is a goal to keep compatibility we don't do that at the moment. // So there is trade off - more stability or more speed. Or rewriting implant logic. // At the moment, i see it affects only `shell` command and locking it for 10 seconds on exit. Not a big deal. func (t *tunnels) ScheduleClose(tunnelID uint64) { @@ -153,7 +153,7 @@ func (t *tunnels) ScheduleClose(tunnelID uint64) { } // Close - closing tunnel -// It's prefered to use ScheduleClose function if you don't 100% sure there is no more data to receive +// It's preferred to use ScheduleClose function if you don't 100% sure there is no more data to receive func (t *tunnels) Close(tunnelID uint64) error { t.mutex.Lock() defer t.mutex.Unlock() diff --git a/server/handlers/sessions.go b/server/handlers/sessions.go index b84666fc52..c66f41365f 100644 --- a/server/handlers/sessions.go +++ b/server/handlers/sessions.go @@ -111,7 +111,7 @@ func tunnelDataHandler(implantConn *core.ImplantConnection, data []byte) *sliver tunnelData := &sliverpb.TunnelData{} proto.Unmarshal(data, tunnelData) - sessionHandlerLog.Debugf("[DATA] Sequence on tunel %d, %d, data: %s", tunnelData.TunnelID, tunnelData.Sequence, tunnelData.Data) + sessionHandlerLog.Debugf("[DATA] Sequence on tunnel %d, %d, data: %s", tunnelData.TunnelID, tunnelData.Sequence, tunnelData.Data) tunnel := core.Tunnels.Get(tunnelData.TunnelID) if tunnel != nil { @@ -137,7 +137,7 @@ func tunnelCloseHandler(implantConn *core.ImplantConnection, data []byte) *slive tunnelData := &sliverpb.TunnelData{} proto.Unmarshal(data, tunnelData) - sessionHandlerLog.Debugf("[CLOSE] Sequence on tunel %d, %d, data: %s", tunnelData.TunnelID, tunnelData.Sequence, tunnelData.Data) + sessionHandlerLog.Debugf("[CLOSE] Sequence on tunnel %d, %d, data: %s", tunnelData.TunnelID, tunnelData.Sequence, tunnelData.Data) if !tunnelData.Closed { return nil } @@ -181,10 +181,10 @@ func socksDataHandler(implantConn *core.ImplantConnection, data []byte) *sliverp // return nil //} sessionHandlerLog.Debugf("socksDataHandler:", len(socksData.Data), socksData.Data) - SocksTunne := core.SocksTunnels.Get(socksData.TunnelID) - if SocksTunne != nil { - if session.ID == SocksTunne.SessionID { - SocksTunne.FromImplant <- socksData + socksTunnel := core.SocksTunnels.Get(socksData.TunnelID) + if socksTunnel != nil { + if session.ID == socksTunnel.SessionID { + socksTunnel.FromImplant <- socksData } else { sessionHandlerLog.Warnf("Warning: Session %s attempted to send data on tunnel it did not own", session.ID) } diff --git a/server/sgn/sgn.go b/server/sgn/sgn.go index 0f7281fb44..d7c0a6effd 100644 --- a/server/sgn/sgn.go +++ b/server/sgn/sgn.go @@ -1,5 +1,23 @@ package sgn +/* + 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 . +*/ + import ( "bytes" "errors"