diff --git a/internal/runner/runner.go b/internal/runner/runner.go index ed0bcb59..89780a66 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -49,6 +49,10 @@ func New(options *clients.Options) (*Runner, error) { if options.OpenSSLBinary != "" { openssl.UseOpenSSLBinary(options.OpenSSLBinary) } + if options.TlsCiphersEnum { + // cipher enumeration requires tls versions + options.TlsVersionsEnum = true + } showBanner() if options.Version { diff --git a/pkg/output/output.go b/pkg/output/output.go index 00a41c78..6009ecbb 100644 --- a/pkg/output/output.go +++ b/pkg/output/output.go @@ -2,6 +2,7 @@ package output import ( "bytes" + "fmt" "os" "regexp" "strings" @@ -147,7 +148,7 @@ func (w *StandardWriter) formatStandard(output *clients.Response) ([]byte, error } } - if !w.options.SAN && !w.options.CN { + if !w.options.SAN && !w.options.CN && !w.options.TlsCiphersEnum { builder.WriteString(outputPrefix) } if !output.ProbeStatus { @@ -235,7 +236,12 @@ func (w *StandardWriter) formatStandard(output *clients.Response) ([]byte, error builder.WriteString("]") } - if w.options.TlsVersionsEnum { + if w.options.TlsCiphersEnum { + for _, v := range output.TlsCiphers { + builder.WriteString(outputPrefix) + builder.WriteString(fmt.Sprintf(" [%v] [%v]\n", w.aurora.BrightBlue(v.Version), w.aurora.BrightGreen(strings.Join(v.Ciphers, ",")))) + } + } else if w.options.TlsVersionsEnum { builder.WriteString(" [") builder.WriteString(w.aurora.Magenta(strings.Join(output.VersionEnum, ",")).String()) builder.WriteString("]") diff --git a/pkg/tlsx/auto/util.go b/pkg/tlsx/auto/util.go index 6921cf13..d906f004 100644 --- a/pkg/tlsx/auto/util.go +++ b/pkg/tlsx/auto/util.go @@ -1,6 +1,7 @@ package auto import ( + "github.com/projectdiscovery/tlsx/pkg/tlsx/openssl" "github.com/projectdiscovery/tlsx/pkg/tlsx/tls" "github.com/projectdiscovery/tlsx/pkg/tlsx/ztls" sliceutil "github.com/projectdiscovery/utils/slice" @@ -13,7 +14,9 @@ var ( func init() { allCiphersNames = append(tls.AllCiphersNames, ztls.AllCiphersNames...) + allCiphersNames = append(allCiphersNames, openssl.AllCiphersNames...) supportedTlsVersions = append(tls.SupportedTlsVersions, ztls.SupportedTlsVersions...) + supportedTlsVersions = append(supportedTlsVersions, openssl.SupportedTLSVersions...) allCiphersNames = sliceutil.Dedupe(allCiphersNames) supportedTlsVersions = sliceutil.Dedupe(supportedTlsVersions) } diff --git a/pkg/tlsx/clients/clients.go b/pkg/tlsx/clients/clients.go index 6e7f706f..e9bda7b5 100644 --- a/pkg/tlsx/clients/clients.go +++ b/pkg/tlsx/clients/clients.go @@ -387,10 +387,19 @@ func PemEncode(cert []byte) string { return buf.String() } +type EnumMode uint + +const ( + None EnumMode = iota + Version + Cipher +) + type ConnectOptions struct { SNI string VersionTLS string Ciphers []string + EnumMode EnumMode // Enumeration Mode (version or ciphers) } // ParseASN1DNSequenceWithZpkixOrDefault return the parsed value of ASN1DNSequence or a default string value diff --git a/pkg/tlsx/openssl/openssl.go b/pkg/tlsx/openssl/openssl.go index d251c630..8ac49a51 100644 --- a/pkg/tlsx/openssl/openssl.go +++ b/pkg/tlsx/openssl/openssl.go @@ -11,6 +11,7 @@ import ( "github.com/projectdiscovery/tlsx/pkg/tlsx/clients" errorutils "github.com/projectdiscovery/utils/errors" iputil "github.com/projectdiscovery/utils/ip" + stringsutil "github.com/projectdiscovery/utils/strings" ) // Client is a TLS grabbing client using crypto/tls @@ -44,13 +45,30 @@ func (c *Client) ConnectWithOptions(hostname, ip, port string, options clients.C return nil, errorutils.NewWithTag("openssl", "client requires valid address got port=%v,hostname=%v,ip=%v", port, hostname, ip) } + // In enum mode return if given options are not supported + if options.EnumMode == clients.Version && (options.VersionTLS == "" || !stringsutil.EqualFoldAny(options.VersionTLS, SupportedTLSVersions...)) { + // version not supported + return nil, errorutils.NewWithTag("openssl", "tlsversion `%v` not supported in openssl", options.VersionTLS) + } + if options.EnumMode == clients.Cipher { + if len(options.Ciphers) == 0 { + return nil, errorutils.NewWithTag("openssl", "missing cipher value in cipher enum mode") + } + if _, err := toOpenSSLCiphers(options.Ciphers...); err != nil { + return nil, errorutils.NewWithErr(err).WithTag("openssl") + } + } + // Note: CLI options are omitted if given value is empty opensslOptions := &Options{ Address: address, ServerName: options.SNI, Protocol: getProtocol(options.VersionTLS), CAFile: c.options.CACertificate, - Cipher: validateCiphers(options.Ciphers...), + } + + if ciphers, _ := toOpenSSLCiphers(options.Ciphers...); len(ciphers) > 0 { + opensslOptions.Cipher = ciphers } if opensslOptions.ServerName == "" { @@ -120,12 +138,12 @@ func (c *Client) ConnectWithOptions(hostname, ip, port string, options clients.C // SupportedTLSVersions is meaningless here but necessary due to the interface system implemented func (c *Client) SupportedTLSVersions() ([]string, error) { - return supportedTLSVersions(), nil + return SupportedTLSVersions, nil } // SupportedTLSVersions is meaningless here but necessary due to the interface system implemented func (c *Client) SupportedTLSCiphers() ([]string, error) { - return fetchCiphers(), nil + return AllCiphersNames, nil } // Openssl s_client does not dump certificate chain unless specified diff --git a/pkg/tlsx/openssl/options.go b/pkg/tlsx/openssl/options.go index 126d8172..6831716d 100644 --- a/pkg/tlsx/openssl/options.go +++ b/pkg/tlsx/openssl/options.go @@ -6,6 +6,14 @@ import ( "strings" ) +// SupportedTLSVersion of OpenSSL Mode +var SupportedTLSVersions = []string{ + "tls10", + "tls11", + "tls12", + // "tls13", +} + type Protocols int const ( @@ -36,16 +44,6 @@ func (p *Protocols) String() string { } } -// supported tls version -func supportedTLSVersions() []string { - return []string{ - "tls10", - "tls11", - "tls12", - // "tls13", - } -} - func getProtocol(versionTLS string) Protocols { var tlsversion Protocols switch versionTLS { @@ -55,15 +53,15 @@ func getProtocol(versionTLS string) Protocols { tlsversion = TLSv1_1 case "tls12": tlsversion = TLSv1_2 - // case "tls13": - // tlsversion = TLSv1_3 - case "dtls10": - tlsversion = DTLSv1 - case "dtls12": - tlsversion = DTLSv1_2 + // case "tls13": + // tlsversion = TLSv1_3 + // case "dtls10": + // tlsversion = DTLSv1 + // case "dtls12": + // tlsversion = DTLSv1_2 } if versionTLS == "" { - // if no tls version is used use tls13 + // if no tls version is used use tls12 // to avoid possible chances of handshake failures return TLSv1_2 } diff --git a/pkg/tlsx/openssl/utils.go b/pkg/tlsx/openssl/utils.go index b445bebb..3c77d889 100644 --- a/pkg/tlsx/openssl/utils.go +++ b/pkg/tlsx/openssl/utils.go @@ -7,29 +7,23 @@ import ( errorutil "github.com/projectdiscovery/utils/errors" ) -// AllCiphers -var AllCiphers map[string]struct{} = map[string]struct{}{} +// AllCipherNames contains all ciphers supported by openssl +var AllCiphersNames []string = []string{} -// returns array of openssl Ciphers -func fetchCiphers() []string { - arr := []string{} - for k := range AllCiphers { - arr = append(arr, k) - } - return arr -} +// cipherMap +var cipherMap map[string]struct{} = map[string]struct{}{} // validate given ciphers and -func validateCiphers(cipher ...string) []string { +func toOpenSSLCiphers(cipher ...string) ([]string, error) { arr := []string{} for _, v := range cipher { - if _, ok := AllCiphers[v]; ok { + if _, ok := cipherMap[v]; ok { arr = append(arr, v) } else { - gologger.Debug().Label("openssl").Msgf("does not support %v cipher. skipping..", v) + return arr, errorutil.NewWithTag("openssl", "cipher suite %v not supported", v) } } - return arr + return arr, nil } func parseSessionValue(line string) string { @@ -59,6 +53,7 @@ func init() { gologger.Debug().Label("openssl").Msg(err.Error()) } for _, v := range ciphers { - AllCiphers[v] = struct{}{} + cipherMap[v] = struct{}{} + AllCiphersNames = append(AllCiphersNames, v) } } diff --git a/pkg/tlsx/tls/tls.go b/pkg/tlsx/tls/tls.go index e23b637d..120a21a8 100644 --- a/pkg/tlsx/tls/tls.go +++ b/pkg/tlsx/tls/tls.go @@ -16,6 +16,7 @@ import ( "github.com/projectdiscovery/tlsx/pkg/tlsx/clients" errorutil "github.com/projectdiscovery/utils/errors" iputil "github.com/projectdiscovery/utils/ip" + stringsutil "github.com/projectdiscovery/utils/strings" "github.com/rs/xid" ) @@ -107,6 +108,23 @@ func (c *Client) ConnectWithOptions(hostname, ip, port string, options clients.C return nil, errorutil.NewWithTag("ctls", "client requires valid address got port=%v,hostname=%v,ip=%v", port, hostname, ip) } + // In enum mode return if given options are not supported + if options.EnumMode == clients.Version && (options.VersionTLS == "" || !stringsutil.EqualFoldAny(options.VersionTLS, SupportedTlsVersions...)) { + // version not supported + return nil, errorutil.NewWithTag("ctls", "tlsversion `%v` not supported in ctls", options.VersionTLS) + } + if options.EnumMode == clients.Cipher { + if options.VersionTLS == "tls13" { + return nil, errorutil.NewWithTag("ctls", "cipher enum not supported in ctls with tls1.3") + } + if len(options.Ciphers) == 0 { + return nil, errorutil.NewWithTag("ctls", "missing cipher value in cipher enum mode") + } + if _, err := toTLSCiphers(options.Ciphers); err != nil { + return nil, errorutil.NewWithErr(err).WithTag("ctls") + } + } + ctx := context.Background() if c.options.Timeout != 0 { var cancel context.CancelFunc diff --git a/pkg/tlsx/tls/utils.go b/pkg/tlsx/tls/utils.go index f7bdad0a..7165a684 100644 --- a/pkg/tlsx/tls/utils.go +++ b/pkg/tlsx/tls/utils.go @@ -2,7 +2,8 @@ package tls import ( "crypto/tls" - "fmt" + + errorutil "github.com/projectdiscovery/utils/errors" ) var ( @@ -26,7 +27,7 @@ func toTLSCiphers(items []string) ([]uint16, error) { for _, item := range items { cipher, ok := tlsCiphers[item] if !ok { - return nil, fmt.Errorf("unsupported cipher suite: %s", item) + return nil, errorutil.NewWithTag("ctls", "cipher suite %v not supported", item) } convertedCiphers = append(convertedCiphers, cipher) } diff --git a/pkg/tlsx/tlsx.go b/pkg/tlsx/tlsx.go index 18f3932f..ec183b0c 100644 --- a/pkg/tlsx/tlsx.go +++ b/pkg/tlsx/tlsx.go @@ -94,6 +94,7 @@ func (s *Service) ConnectWithOptions(host, ip, port string, options clients.Conn } if s.options.TlsVersionsEnum { + options.EnumMode = clients.Version supportedTlsVersions := []string{resp.Version} enumeratedTlsVersions, _ := s.enumTlsVersions(host, ip, port, options) supportedTlsVersions = append(supportedTlsVersions, enumeratedTlsVersions...) @@ -102,6 +103,7 @@ func (s *Service) ConnectWithOptions(host, ip, port string, options clients.Conn var supportedTlsCiphers []clients.TlsCiphers if s.options.TlsCiphersEnum { + options.EnumMode = clients.Cipher for _, supportedTlsVersion := range resp.VersionEnum { options.VersionTLS = supportedTlsVersion enumeratedTlsVersions, _ := s.enumTlsCiphers(host, ip, port, options) diff --git a/pkg/tlsx/ztls/utils.go b/pkg/tlsx/ztls/utils.go index 648d4e7e..bcf9c64a 100644 --- a/pkg/tlsx/ztls/utils.go +++ b/pkg/tlsx/ztls/utils.go @@ -1,8 +1,7 @@ package ztls import ( - "fmt" - + errorutil "github.com/projectdiscovery/utils/errors" "github.com/zmap/zcrypto/tls" ) @@ -27,7 +26,7 @@ func toZTLSCiphers(items []string) ([]uint16, error) { for _, item := range items { zcipher, ok := ztlsCiphers[item] if !ok { - return nil, fmt.Errorf("unsupported cipher suite: %s", item) + return nil, errorutil.NewWithTag("ztls", "cipher suite %v not supported", item) } convertedCiphers = append(convertedCiphers, zcipher) } diff --git a/pkg/tlsx/ztls/ztls.go b/pkg/tlsx/ztls/ztls.go index 239b873a..64642719 100644 --- a/pkg/tlsx/ztls/ztls.go +++ b/pkg/tlsx/ztls/ztls.go @@ -15,6 +15,7 @@ import ( "github.com/projectdiscovery/tlsx/pkg/tlsx/ztls/ja3" errorutil "github.com/projectdiscovery/utils/errors" iputil "github.com/projectdiscovery/utils/ip" + stringsutil "github.com/projectdiscovery/utils/strings" "github.com/rs/xid" "github.com/zmap/zcrypto/tls" "github.com/zmap/zcrypto/x509" @@ -116,6 +117,20 @@ func (c *Client) ConnectWithOptions(hostname, ip, port string, options clients.C return nil, errorutil.NewWithTag("ztls", "client requires valid address got port=%v,hostname=%v,ip=%v", port, hostname, ip) } + // In enum mode return if given options are not supported + if options.EnumMode == clients.Version && (options.VersionTLS == "" || !stringsutil.EqualFoldAny(options.VersionTLS, SupportedTlsVersions...)) { + // version not supported + return nil, errorutil.NewWithTag("ztls", "tlsversion `%v` not supported in ctls", options.VersionTLS) + } + if options.EnumMode == clients.Cipher { + if len(options.Ciphers) == 0 { + return nil, errorutil.NewWithTag("ztls", "missing cipher value in cipher enum mode") + } + if _, err := toZTLSCiphers(options.Ciphers); err != nil { + return nil, errorutil.NewWithErr(err).WithTag("ztls") + } + } + if c.options.ScanAllIPs || len(c.options.IPVersion) > 0 { address = net.JoinHostPort(ip, port) }