From 8838439f304e322c4aebbe63d3c3eae09b3baaae Mon Sep 17 00:00:00 2001 From: Tarun Koyalwar Date: Tue, 4 Jul 2023 20:53:57 +0530 Subject: [PATCH] add ztls fallback support --- README.md | 9 +++++++ conn.go | 57 ++++++++++++++++++++++++++++++++++-------- example/simple/main.go | 31 +++++++++++++++++++++++ 3 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 example/simple/main.go diff --git a/README.md b/README.md index 25e6968..7e77147 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ rawhttp is a Go package for making HTTP requests in a raw way. - Forked and adapted from [https://github.com/gorilla/http](https://github.com/gorilla/http) and [https://github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) - The original idea is inspired by [@tomnomnom/rawhttp](https://github.com/tomnomnom/rawhttp) work + +### ZTLS fallback support + +### ZTLS Fallback + +`rawhttp` by default fallbacks to using zcrypto when there is an error in TLS handshake (ex: ` insufficient security level` etc ). This is done to support older TLS versions and ciphers. This can be disabled by setting `rawhttp.DisableZtlsFallback` to `true` or by using `DISABLE_ZTLS_FALLBACK` environment variable. when falling back to ztls, `ChromeCiphers` are used + + + # Example First you need to declare a `server` diff --git a/conn.go b/conn.go index d7c7c2c..7e6085e 100644 --- a/conn.go +++ b/conn.go @@ -3,18 +3,25 @@ package rawhttp import ( "context" "crypto/tls" + "errors" "fmt" "io" "net" "net/url" + "os" "strings" "sync" "time" "github.com/projectdiscovery/rawhttp/client" "github.com/projectdiscovery/rawhttp/proxy" + ztls "github.com/zmap/zcrypto/tls" ) +// DisableZtlsFallback disables ztls fallback when tls handshake fails +// can also be set using the environment variable DISABLE_ZTLS_FALLBACK +var DisableZtlsFallback = false + // Dialer can dial a remote HTTP server. type Dialer interface { // Dial dials a remote http server returning a Conn. @@ -111,21 +118,33 @@ func clientDial(protocol, addr string, timeout time.Duration, options *Options) } // https - tlsConfig := &tls.Config{InsecureSkipVerify: true} + tlsConfig := &tls.Config{InsecureSkipVerify: true, Renegotiation: tls.RenegotiateOnceAsClient} if options.SNI != "" { tlsConfig.ServerName = options.SNI } + + // currently fastdialer tls dial and ztls fallback are mutually exclusive + // TODO: add support for fallback in fastDialer.DialZTLS() if options.FastDialer != nil { return options.FastDialer.DialTLSWithConfig(ctx, "tcp", addr, tlsConfig) - } else if timeout > 0 { - conn, err := net.DialTimeout("tcp", addr, timeout) - if err != nil { - return nil, err - } - tlsConn := tls.Client(conn, tlsConfig) - return tlsConn, tlsConn.HandshakeContext(ctx) + } - return tls.Dial("tcp", addr, tlsConfig) + + var dialer *net.Dialer + if timeout > 0 { + dialer = &net.Dialer{Timeout: timeout} + } else { + dialer = &net.Dialer{Timeout: 8 * time.Second} // should be more than enough + } + tlsConn, err := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig) + if err != nil && !DisableZtlsFallback && !errors.Is(err, os.ErrDeadlineExceeded) { + return ztls.DialWithDialer(dialer, "tcp", addr, &ztls.Config{ + CipherSuites: ztls.ChromeCiphers, + ServerName: tlsConfig.ServerName, + InsecureSkipVerify: true, + }) + } + return tlsConn, err } // TlsHandshake tls handshake on a plain connection @@ -152,7 +171,18 @@ func TlsHandshake(conn net.Conn, addr string, timeout time.Duration) (net.Conn, ServerName: hostname, }) if err := tlsConn.HandshakeContext(ctx); err != nil { - conn.Close() + if !errors.Is(err, os.ErrDeadlineExceeded) && !DisableZtlsFallback { + // fallback to ztls + ztlsConn := ztls.Client(conn, &ztls.Config{ + InsecureSkipVerify: true, + ServerName: hostname, + CipherSuites: ztls.ChromeCiphers, + }) + if err := ztlsConn.Handshake(); err != nil { + return nil, err + } + return ztlsConn, nil + } return nil, err } return tlsConn, nil @@ -181,3 +211,10 @@ func (c *conn) Release() { addr := c.Conn.RemoteAddr().String() c.dialer.conns[addr] = append(c.dialer.conns[addr], c) } + +func init() { + value := os.Getenv("DISABLE_ZTLS_FALLBACK") + if strings.EqualFold(value, "true") { + DisableZtlsFallback = true + } +} diff --git a/example/simple/main.go b/example/simple/main.go new file mode 100644 index 0000000..27783f6 --- /dev/null +++ b/example/simple/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "flag" + "fmt" + "net/http/httputil" + + "github.com/projectdiscovery/rawhttp" +) + +var ( + url string + short bool +) + +func main() { + flag.StringVar(&url, "url", "https://scanme.sh", "URL to fetch") + flag.BoolVar(&short, "short", false, "Skip printing http response body") + flag.Parse() + + client := rawhttp.NewClient(rawhttp.DefaultOptions) + resp, err := client.Get(url) + if err != nil { + panic(err) + } + bin, err := httputil.DumpResponse(resp, !short) + if err != nil { + panic(err) + } + fmt.Println(string(bin)) +}