diff --git a/README.md b/README.md index c2504fd3..184705f0 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,9 @@ PROBES: -tps, -probe-status display tls probe status MISCONFIGURATIONS: - -ex, -expired display validity status of certificate - -ss, -self-signed display status of self-signed certificate + -ex, -expired display expired certificate + -ss, -self-signed display self-signed certificate + -mm, -mismatched display mismatched certificate CONFIGURATIONS: -config string path to the tlsx configuration file @@ -290,12 +291,14 @@ www.hackerone.com:443 [TLS1.3] [TLS_AES_128_GCM_SHA256] support.hackerone.com:443 [TLS1.2] [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256] ``` -### Expired / Self Signed Certificate +# TLS Misconfiguration -A list of host can be provided to tlsx to detect **expired / self-signed** certificates. +### Expired / Self Signed / Mismatched Certificate + +A list of host can be provided to tlsx to detect **expired / self-signed / mismatched** certificates. ```console -$ tlsx -u expired.badssl.com,self-signed.badssl.com -expired -self-signed +$ tlsx -l hosts.txt -expired -self-signed -mismatched _____ _ _____ __ @@ -308,8 +311,9 @@ $ tlsx -u expired.badssl.com,self-signed.badssl.com -expired -self-signed [WRN] Use with caution. You are responsible for your actions. [WRN] Developers assume no liability and are not responsible for any misuse or damage. -expired.badssl.com:443 [expired] +wrong.host.badssl.com:443 [mismatched] self-signed.badssl.com:443 [self-signed] +expired.badssl.com:443 [expired] ``` ### JSON Output diff --git a/cmd/tlsx/main.go b/cmd/tlsx/main.go index 984e00a3..45cad260 100644 --- a/cmd/tlsx/main.go +++ b/cmd/tlsx/main.go @@ -66,8 +66,9 @@ func readFlags() error { ) flagSet.CreateGroup("misconfigurations", "Misconfigurations", - flagSet.BoolVarP(&options.Expired, "expired", "ex", false, "display validity status of certificate"), - flagSet.BoolVarP(&options.SelfSigned, "self-signed", "ss", false, "display status of self-signed certificate"), + flagSet.BoolVarP(&options.Expired, "expired", "ex", false, "display expired certificate"), + flagSet.BoolVarP(&options.SelfSigned, "self-signed", "ss", false, "display self-signed certificate"), + flagSet.BoolVarP(&options.MisMatched, "mismatched", "mm", false, "display mismatched certificate"), ) flagSet.CreateGroup("configs", "Configurations", @@ -75,7 +76,7 @@ func readFlags() error { flagSet.StringSliceVarP(&options.Resolvers, "resolvers", "r", nil, "list of resolvers to use", goflags.FileCommaSeparatedStringSliceOptions), flagSet.StringVarP(&options.CACertificate, "cacert", "cc", "", "client certificate authority file"), flagSet.StringSliceVarP(&options.Ciphers, "cipher-input", "ci", nil, "ciphers to use with tls connection", goflags.FileCommaSeparatedStringSliceOptions), - flagSet.StringVar(&options.ServerName, "sni", "", "tls sni hostname to use"), + flagSet.StringSliceVar(&options.ServerName, "sni", nil, "tls sni hostname to use", goflags.FileCommaSeparatedStringSliceOptions), flagSet.StringVar(&options.MinVersion, "min-version", "", "minimum tls version to accept (ssl30,tls10,tls11,tls12,tls13)"), flagSet.StringVar(&options.MaxVersion, "max-version", "", "maximum tls version to accept (ssl30,tls10,tls11,tls12,tls13)"), flagSet.BoolVarP(&options.TLSChain, "tls-chain", "tc", false, "display tls chain in json output"), diff --git a/go.mod b/go.mod index 2f101e4d..0aab37fd 100644 --- a/go.mod +++ b/go.mod @@ -8,22 +8,27 @@ require ( github.com/logrusorgru/aurora v2.0.3+incompatible github.com/pkg/errors v0.9.1 github.com/projectdiscovery/fastdialer v0.0.16-0.20220620143737-2ba20b53770a - github.com/projectdiscovery/fileutil v0.0.0-20220506114156-c4ab20801483 + github.com/projectdiscovery/fileutil v0.0.0-20220609150212-453ac591c36c github.com/projectdiscovery/goflags v0.0.8 github.com/projectdiscovery/gologger v1.1.4 github.com/projectdiscovery/iputil v0.0.0-20220613112553-9b6873b2c619 - github.com/projectdiscovery/mapcidr v1.0.0 + github.com/projectdiscovery/mapcidr v1.0.1 + github.com/projectdiscovery/stringsutil v0.0.0-20220612082425-0037ce9f89f3 github.com/rs/xid v1.4.0 github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c + go.uber.org/multierr v1.8.0 ) require ( github.com/akrylysov/pogreb v0.10.1 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/aymerick/douceur v0.2.0 // indirect github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/karrick/godirwalk v1.16.1 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/microcosm-cc/bluemonday v1.0.18 // indirect github.com/miekg/dns v1.1.43 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -33,16 +38,17 @@ require ( github.com/projectdiscovery/networkpolicy v0.0.1 // indirect github.com/projectdiscovery/retryabledns v1.0.13-0.20210916165024-76c5b76fd59a // indirect github.com/projectdiscovery/retryablehttp-go v1.0.2 // indirect - github.com/projectdiscovery/stringsutil v0.0.0-20220422150559-b54fb5dc6833 // indirect + github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect github.com/syndtr/goleveldb v1.0.0 // indirect github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 // indirect github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236 // indirect github.com/yl2chen/cidranger v1.0.2 // indirect github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect go.etcd.io/bbolt v1.3.6 // indirect + go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 046b2a9e..35d39567 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ= github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -13,8 +15,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -35,6 +38,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/hdm/jarm-go v0.0.7 h1:Eq0geenHrBSYuKrdVhrBdMMzOmA+CAMLzN2WrF3eL6A= github.com/hdm/jarm-go v0.0.7/go.mod h1:kinGoS0+Sdn1Rr54OtanET5E5n7AlD6T6CrJAKDjJSQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -42,7 +47,6 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -56,6 +60,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= +github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= @@ -94,8 +100,9 @@ github.com/projectdiscovery/cryptoutil v0.0.0-20210805184155-b5d2512f9345/go.mod github.com/projectdiscovery/fastdialer v0.0.16-0.20220620143737-2ba20b53770a h1:LiekfR8hqK3bWr9aE3VU3gPofdBgN4OVvJkIVejlSYM= github.com/projectdiscovery/fastdialer v0.0.16-0.20220620143737-2ba20b53770a/go.mod h1:AJ2E9V04tk1jMQ6ffgqudfSoO2rcQvM7L9Uc6a9UmmQ= github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0= -github.com/projectdiscovery/fileutil v0.0.0-20220506114156-c4ab20801483 h1:0as/e36wk1cnnqK3FojbdOx7fsm9yH6Gy5q9v6Sg/88= github.com/projectdiscovery/fileutil v0.0.0-20220506114156-c4ab20801483/go.mod h1:wjS/oBWbzlayJ/aTK0KW0oOHGO03G8oEYzuN6stI8Ho= +github.com/projectdiscovery/fileutil v0.0.0-20220609150212-453ac591c36c h1:/+xJK8e/Gj/zAmudWh5l2SzGJB+CkwYnraelCkBe7Aw= +github.com/projectdiscovery/fileutil v0.0.0-20220609150212-453ac591c36c/go.mod h1:g8wsrb0S5NtEN0JgVyyPeb3FQdArx+UMESmFX94bcGY= github.com/projectdiscovery/goflags v0.0.8-0.20220426153734-2ffbfbff923c/go.mod h1:uN+pHMLsWQoiZHUg/l0tqf/VdbX3+ecKfYz/H7b/+NA= github.com/projectdiscovery/goflags v0.0.8 h1:IhTmnEGSKtBHfD23tSwpnzai5CRYfLKVOWlVq9ngYvY= github.com/projectdiscovery/goflags v0.0.8/go.mod h1:GDSkWyXa6kfQjpJu10SO64DN8lXuKXVENlBMk8N7H80= @@ -111,35 +118,43 @@ github.com/projectdiscovery/iputil v0.0.0-20220613112553-9b6873b2c619 h1:PSYGmqV github.com/projectdiscovery/iputil v0.0.0-20220613112553-9b6873b2c619/go.mod h1:w5bXH/PHqM8/vF0bFN6fZMggm7IlPKK2dJdz5McY+Qw= github.com/projectdiscovery/mapcidr v0.0.4/go.mod h1:ALOIj6ptkWujNoX8RdQwB2mZ+kAmKuLJBq9T5gR5wG0= github.com/projectdiscovery/mapcidr v0.0.6/go.mod h1:ZEBhMmBU3laUl3g9QGTrzJku1VJOzjdFwW01f/zVVzM= -github.com/projectdiscovery/mapcidr v1.0.0 h1:4ctYAgkxaLeky3o3c+qMgHoUjytYy6DME2fjbgiB1Ag= github.com/projectdiscovery/mapcidr v1.0.0/go.mod h1:5QkKrV6rNQQurCZI3nNedFsAOYp04mRDkC5yht+znYA= +github.com/projectdiscovery/mapcidr v1.0.1 h1:eaLBRrImwlYXv8vbXTwR4sxoQqIxR3Y5k/Sd7HhTIII= +github.com/projectdiscovery/mapcidr v1.0.1/go.mod h1:/qxlpxXZQFFjHynSc9u5O0kUPzH46VskECiwLiz7/vw= github.com/projectdiscovery/networkpolicy v0.0.1 h1:RGRuPlxE8WLFF9tdKSjTsYiTIKHNHW20Kl0nGGiRb1I= github.com/projectdiscovery/networkpolicy v0.0.1/go.mod h1:asvdg5wMy3LPVMGALatebKeOYH5n5fV5RCTv6DbxpIs= github.com/projectdiscovery/retryabledns v1.0.13-0.20210916165024-76c5b76fd59a h1:WJQjr9qi/VjWhdNiGyNqcFi0967Gp0W3I769bCpHOJE= github.com/projectdiscovery/retryabledns v1.0.13-0.20210916165024-76c5b76fd59a/go.mod h1:tXaLDs4n3pRZHwfa8mdXpUWe/AYDNK3HlWDjldhRbjI= github.com/projectdiscovery/retryablehttp-go v1.0.2 h1:LV1/KAQU+yeWhNVlvveaYFsjBYRwXlNEq0PvrezMV0U= github.com/projectdiscovery/retryablehttp-go v1.0.2/go.mod h1:dx//aY9V247qHdsRf0vdWHTBZuBQ2vm6Dq5dagxrDYI= +github.com/projectdiscovery/sliceutil v0.0.0-20220617151003-15892688e1d6 h1:zZmLOwj2duDafEO9i1KqmHMW0SL9+Wwmco9sxhBCVwQ= +github.com/projectdiscovery/sliceutil v0.0.0-20220617151003-15892688e1d6/go.mod h1:9YZb6LRjLYAvSOm65v787dwauurixSyjlqXyYa4rTTA= github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= github.com/projectdiscovery/stringsutil v0.0.0-20210823090203-2f5f137e8e1d/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= -github.com/projectdiscovery/stringsutil v0.0.0-20220422150559-b54fb5dc6833 h1:yo7hCL47BOHl8X/aMmPeRQwiqUrH6TZ2WjgqItaSPcc= github.com/projectdiscovery/stringsutil v0.0.0-20220422150559-b54fb5dc6833/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= +github.com/projectdiscovery/stringsutil v0.0.0-20220612082425-0037ce9f89f3 h1:EbP+rVR7NPsFKfwhzshmXP2GMjqBpaMZurJzQ5PYvbY= +github.com/projectdiscovery/stringsutil v0.0.0-20220612082425-0037ce9f89f3/go.mod h1:mF5sh4jTghoGWwgUb9qWi5waTFklClDbtrqtJU93awc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= +github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 h1:TtyC78WMafNW8QFfv3TeP3yWNDG+uxNkk9vOrnDu6JA= @@ -159,6 +174,10 @@ github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c/go.mod h1:egdRkzUylAT go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -181,8 +200,10 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210414194228-064579744ee0/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210521195947-fe42d452be8f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -210,8 +231,10 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/internal/runner/banner.go b/internal/runner/banner.go index 0f55c4eb..9175af57 100644 --- a/internal/runner/banner.go +++ b/internal/runner/banner.go @@ -17,13 +17,13 @@ var banner = fmt.Sprintf(` |_| |____|___/_/\_\ %s `, version) -var version = "v0.0.3" +var version = "v0.0.4" // validateOptions validates the provided options for crawler func (r *Runner) validateOptions() error { r.hasStdin = fileutil.HasStdin() - probeSpecified := r.options.SO || r.options.TLSVersion || r.options.Cipher || r.options.Expired || r.options.SelfSigned || r.options.Hash != "" || r.options.Jarm + probeSpecified := r.options.SO || r.options.TLSVersion || r.options.Cipher || r.options.Expired || r.options.SelfSigned || r.options.Hash != "" || r.options.Jarm || r.options.MisMatched if r.options.RespOnly && probeSpecified { return errors.New("resp-only flag can only be used with san and cn flags") } diff --git a/internal/runner/runner.go b/internal/runner/runner.go index c8c2780b..f180f0ee 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -25,7 +25,6 @@ import ( type Runner struct { hasStdin bool outputWriter output.Writer - tlsxService *tlsx.Service fastDialer *fastdialer.Dialer options *clients.Options } @@ -70,11 +69,6 @@ func New(options *clients.Options) (*Runner, error) { } runner.outputWriter = outputWriter - tlsxService, err := tlsx.New(options) - if err != nil { - return nil, errors.Wrap(err, "could not create tlsx client") - } - runner.tlsxService = tlsxService return runner, nil } @@ -88,6 +82,7 @@ func (r *Runner) Close() error { type taskInput struct { host string port string + sni string } func (t taskInput) Address() string { @@ -124,11 +119,18 @@ func (r *Runner) Execute() error { func (r *Runner) processInputElementWorker(inputs chan taskInput, wg *sync.WaitGroup) { defer wg.Done() + tlsxService, err := tlsx.New(r.options) + if err != nil { + gologger.Fatal().Msgf("could not create tlsx client: %s", err) + return + } + for task := range inputs { if r.options.Verbose { gologger.Info().Msgf("Processing input %s:%s", task.host, task.port) } - response, err := r.tlsxService.Connect(task.host, task.port) + + response, err := tlsxService.ConnectWithOptions(task.host, task.port, clients.ConnectOptions{SNI: task.sni}) if err != nil { gologger.Warning().Msgf("Could not connect input %s: %s", task.Address(), err) } @@ -185,7 +187,7 @@ func (r *Runner) processInputItem(input string, inputs chan taskInput) { } for cidr := range cidrInputs { for _, port := range r.options.Ports { - inputs <- taskInput{host: cidr, port: port} + r.processInputItemWithSni(taskInput{host: cidr, port: port}, inputs) } } } else { @@ -193,14 +195,25 @@ func (r *Runner) processInputItem(input string, inputs chan taskInput) { host, customPort := r.getHostPortFromInput(input) if customPort == "" { for _, port := range r.options.Ports { - inputs <- taskInput{host: host, port: port} + r.processInputItemWithSni(taskInput{host: host, port: port}, inputs) } } else { - inputs <- taskInput{host: host, port: customPort} + r.processInputItemWithSni(taskInput{host: host, port: customPort}, inputs) } } } +func (r *Runner) processInputItemWithSni(task taskInput, inputs chan taskInput) { + if len(r.options.ServerName) > 0 { + for _, serverName := range r.options.ServerName { + task.sni = serverName + inputs <- task + } + } else { + inputs <- task + } +} + // getHostPortFromInput returns host and optionally port from input. // If no ports are found, port field is left blank and user specified ports // are used. diff --git a/pkg/connpool/inflight.go b/pkg/connpool/inflight.go new file mode 100644 index 00000000..b7c354e2 --- /dev/null +++ b/pkg/connpool/inflight.go @@ -0,0 +1,47 @@ +package connpool + +import ( + "net" + "sync" + + "go.uber.org/multierr" +) + +type InFlightConns struct { + sync.RWMutex + inflightConns map[net.Conn]struct{} +} + +func NewInFlightConns() (*InFlightConns, error) { + return &InFlightConns{inflightConns: make(map[net.Conn]struct{})}, nil +} + +func (i *InFlightConns) Add(conn net.Conn) { + i.Lock() + defer i.Unlock() + + i.inflightConns[conn] = struct{}{} +} + +func (i *InFlightConns) Remove(conn net.Conn) { + i.Lock() + defer i.Unlock() + + delete(i.inflightConns, conn) +} + +func (i *InFlightConns) Close() error { + i.Lock() + defer i.Unlock() + + var errs []error + + for conn := range i.inflightConns { + if err := conn.Close(); err != nil { + errs = append(errs, err) + } + delete(i.inflightConns, conn) + } + + return multierr.Combine(errs...) +} diff --git a/pkg/connpool/onetimepool.go b/pkg/connpool/onetimepool.go new file mode 100644 index 00000000..62c6d8c9 --- /dev/null +++ b/pkg/connpool/onetimepool.go @@ -0,0 +1,77 @@ +package connpool + +import ( + "context" + "net" + + "github.com/projectdiscovery/fastdialer/fastdialer" +) + +// OneTimePool is a pool designed to create continous bare connections that are for one time only usage +type OneTimePool struct { + address string + idleConnections chan net.Conn + InFlightConns *InFlightConns + ctx context.Context + cancel context.CancelFunc + FastDialer *fastdialer.Dialer +} + +func NewOneTimePool(ctx context.Context, address string, poolSize int) (*OneTimePool, error) { + idleConnections := make(chan net.Conn, poolSize) + inFlightConns, err := NewInFlightConns() + if err != nil { + return nil, err + } + pool := &OneTimePool{ + address: address, + idleConnections: idleConnections, + InFlightConns: inFlightConns, + } + if ctx != nil { + pool.ctx = ctx + } + pool.ctx, pool.cancel = context.WithCancel(ctx) + return pool, nil +} + +// Acquire acquires an idle connection from the pool +func (p *OneTimePool) Acquire(c context.Context) (net.Conn, error) { + select { + case <-p.ctx.Done(): + return nil, p.ctx.Err() + case <-c.Done(): + return nil, c.Err() + case conn := <-p.idleConnections: + p.InFlightConns.Remove(conn) + return conn, nil + } +} + +func (p *OneTimePool) Run() error { + for { + select { + case <-p.ctx.Done(): + return p.ctx.Err() + default: + var ( + conn net.Conn + err error + ) + if p.FastDialer != nil { + conn, err = p.FastDialer.Dial(p.ctx, "tcp", p.address) + } else { + conn, err = net.Dial("tcp", p.address) + } + if err == nil { + p.InFlightConns.Add(conn) + p.idleConnections <- conn + } + } + } +} + +func (p *OneTimePool) Close() error { + p.cancel() + return p.InFlightConns.Close() +} diff --git a/pkg/output/output.go b/pkg/output/output.go index 5fbb1bd7..c033cf44 100644 --- a/pkg/output/output.go +++ b/pkg/output/output.go @@ -70,7 +70,6 @@ func (w *StandardWriter) Write(event *clients.Response) error { w.outputMutex.Lock() defer w.outputMutex.Unlock() - _, _ = os.Stdout.Write(data) _, _ = os.Stdout.Write([]byte("\n")) if w.outputFile != nil { @@ -147,7 +146,12 @@ func (w *StandardWriter) formatStandard(output *clients.Response) ([]byte, error builder.WriteString(w.aurora.Green("success").String()) builder.WriteString("]") } - if w.options.SO && len(cert.SubjectOrg) > 0 { + if w.options.ServerName != nil { + builder.WriteString(" [") + builder.WriteString(w.aurora.Blue(output.ServerName).String()) + builder.WriteString("]") + } + if w.options.SO && len(cert.SubjectOrg) > 0 { builder.WriteString(" [") builder.WriteString(w.aurora.BrightYellow(strings.Join(cert.SubjectOrg, ",")).String()) builder.WriteString("]") @@ -172,6 +176,11 @@ func (w *StandardWriter) formatStandard(output *clients.Response) ([]byte, error builder.WriteString(w.aurora.Yellow("self-signed").String()) builder.WriteString("]") } + if w.options.MisMatched && cert.MisMatched { + builder.WriteString(" [") + builder.WriteString(w.aurora.Yellow("mismatched").String()) + builder.WriteString("]") + } if w.options.Hash != "" { hashOpts := strings.Split(w.options.Hash, ",") diff --git a/pkg/tlsx/auto/auto.go b/pkg/tlsx/auto/auto.go index 6b9ca927..b74351e3 100644 --- a/pkg/tlsx/auto/auto.go +++ b/pkg/tlsx/auto/auto.go @@ -32,11 +32,11 @@ func New(options *clients.Options) (*Client, error) { } // Connect connects to a host and grabs the response data -func (c *Client) Connect(hostname, port string) (*clients.Response, error) { - response, err := c.tlsClient.Connect(hostname, port) +func (c *Client) ConnectWithOptions(hostname, port string, options clients.ConnectOptions) (*clients.Response, error) { + response, err := c.tlsClient.ConnectWithOptions(hostname, port, options) isInvalidResponse := c.isResponseInvalid(response) if err != nil || isInvalidResponse { - ztlsResponse, ztlsErr := c.ztlsClient.Connect(hostname, port) + ztlsResponse, ztlsErr := c.ztlsClient.ConnectWithOptions(hostname, port, options) if ztlsErr != nil { return nil, ztlsErr } diff --git a/pkg/tlsx/clients/clients.go b/pkg/tlsx/clients/clients.go index b7c26cac..1913e06d 100644 --- a/pkg/tlsx/clients/clients.go +++ b/pkg/tlsx/clients/clients.go @@ -7,16 +7,18 @@ import ( "crypto/sha256" "encoding/hex" "math" + "strings" "time" "github.com/projectdiscovery/fastdialer/fastdialer" "github.com/projectdiscovery/goflags" + "github.com/projectdiscovery/stringsutil" ) // Implementation is an interface implemented by TLSX client type Implementation interface { // Connect connects to a host and grabs the response data - Connect(hostname, port string) (*Response, error) + ConnectWithOptions(hostname, port string, options ConnectOptions) (*Response, error) } // Options contains configuration options for tlsx client @@ -28,7 +30,7 @@ type Options struct { // InputList is the list of inputs to process InputList string // ServerName is the optional server-name for tls connection - ServerName string + ServerName goflags.StringSlice // Verbose enables display of verbose output Verbose bool // Version shows the version of the program @@ -84,6 +86,8 @@ type Options struct { Expired bool // SelfSigned displays if cert is self-signed SelfSigned bool + // MisMatched displays if the cert is mismatched + MisMatched bool // Hash is the hash to display for certificate Hash string // Jarm calculate jarm fingerprinting with multiple probes @@ -118,8 +122,9 @@ type Response struct { // when ran using scan-mode auto. TLSConnection string `json:"tls_connection,omitempty"` // Chain is the chain of certificates - Chain []*CertificateResponse `json:"chain,omitempty"` - JarmHash string `json:"jarm_hash,omitempty"` + Chain []*CertificateResponse `json:"chain,omitempty"` + JarmHash string `json:"jarm_hash,omitempty"` + ServerName string `json:"sni,omitempty"` } // CertificateResponse is the response for a certificate @@ -128,6 +133,8 @@ type CertificateResponse struct { Expired bool `json:"expired,omitempty"` // SelfSigned returns true if the certificate is self-signed SelfSigned bool `json:"self_signed,omitempty"` + // MisMatched returns true if the certificate is mismatched + MisMatched bool `json:"mismatched,omitempty"` // NotBefore is the not-before time for certificate NotBefore time.Time `json:"not_before,omitempty"` // NotAfter is the not-after time for certificate @@ -207,3 +214,39 @@ func IsSelfSigned(authorityKeyID, subjectKeyID []byte) bool { } return false } + +// IsMisMatchedCert returns true if cert names(subject common name + alternative names) does not contain host +func IsMisMatchedCert(host string, names []string) bool { + hostTokens := strings.Split(host, ".") + for _, name := range names { + // if not wildcard, return false if name matches the host + if !strings.Contains(name, "*") { + if strings.EqualFold(name, host) { + return false + } + } else { + // try to match the wildcard name with host + nameTokens := strings.Split(name, ".") + if len(hostTokens) == len(nameTokens) { + matched := false + for i, token := range nameTokens { + if stringsutil.EqualFoldAny(token, "*", hostTokens[i]) { + matched = true + } else { + matched = false + break + } + } + // return false if all the name tokens matched the host tokens + if matched { + return false + } + } + } + } + return true +} + +type ConnectOptions struct { + SNI string +} diff --git a/pkg/tlsx/jarm/jarm.go b/pkg/tlsx/jarm/jarm.go index 334cff16..0ef0ae75 100644 --- a/pkg/tlsx/jarm/jarm.go +++ b/pkg/tlsx/jarm/jarm.go @@ -9,31 +9,42 @@ import ( gojarm "github.com/hdm/jarm-go" "github.com/projectdiscovery/fastdialer/fastdialer" + "github.com/projectdiscovery/tlsx/pkg/connpool" ) // fingerprint probes a single host/port func HashWithDialer(dialer *fastdialer.Dialer, host string, port int, timeout time.Duration) (string, error) { results := []string{} + addr := net.JoinHostPort(host, fmt.Sprintf("%d", port)) + // using connection pool as we need multiple probes + pool, err := connpool.NewOneTimePool(context.Background(), addr, 3) + if err != nil { + return "", err + } + pool.FastDialer = dialer + + defer pool.Close() //nolint + go pool.Run() //nolint + for _, probe := range gojarm.GetProbes(host, port) { - addr := net.JoinHostPort(host, fmt.Sprintf("%d", port)) - c, err := dialer.Dial(context.Background(), "tcp", addr) + conn, err := pool.Acquire(context.Background()) if err != nil { continue } - if c == nil { + if conn == nil { continue } - _ = c.SetWriteDeadline(time.Now().Add(timeout)) - _, err = c.Write(gojarm.BuildProbe(probe)) + _ = conn.SetWriteDeadline(time.Now().Add(timeout)) + _, err = conn.Write(gojarm.BuildProbe(probe)) if err != nil { results = append(results, "") - _ = c.Close() + _ = conn.Close() continue } - _ = c.SetReadDeadline(time.Now().Add(timeout)) + _ = conn.SetReadDeadline(time.Now().Add(timeout)) buff := make([]byte, 1484) - _, _ = c.Read(buff) - _ = c.Close() + _, _ = conn.Read(buff) + _ = conn.Close() ans, err := gojarm.ParseServerHello(buff, probe) if err != nil { results = append(results, "") diff --git a/pkg/tlsx/tls/tls.go b/pkg/tlsx/tls/tls.go index 2f9d801f..32a7f490 100644 --- a/pkg/tlsx/tls/tls.go +++ b/pkg/tlsx/tls/tls.go @@ -57,9 +57,6 @@ func New(options *clients.Options) (*Client, error) { options: options, } - if options.ServerName != "" { - c.tlsConfig.ServerName = options.ServerName - } if len(options.Ciphers) > 0 { if customCiphers, err := toTLSCiphers(options.Ciphers); err != nil { return nil, errors.Wrap(err, "could not get tls ciphers") @@ -98,7 +95,7 @@ func New(options *clients.Options) (*Client, error) { } // Connect connects to a host and grabs the response data -func (c *Client) Connect(hostname, port string) (*clients.Response, error) { +func (c *Client) ConnectWithOptions(hostname, port string, options clients.ConnectOptions) (*clients.Response, error) { address := net.JoinHostPort(hostname, port) ctx := context.Background() @@ -120,7 +117,9 @@ func (c *Client) Connect(hostname, port string) (*clients.Response, error) { config := c.tlsConfig if config.ServerName == "" { c := config.Clone() - if iputil.IsIP(hostname) { + if options.SNI != "" { + c.ServerName = options.SNI + } else if iputil.IsIP(hostname) { // using a random sni will return the default server certificate c.ServerName = xid.New().String() } else { @@ -157,17 +156,18 @@ func (c *Client) Connect(hostname, port string) (*clients.Response, error) { Version: tlsVersion, Cipher: tlsCipher, TLSConnection: "ctls", - CertificateResponse: convertCertificateToResponse(leafCertificate), + CertificateResponse: convertCertificateToResponse(hostname, leafCertificate), + ServerName: config.ServerName, } if c.options.TLSChain { for _, cert := range certificateChain { - response.Chain = append(response.Chain, convertCertificateToResponse(cert)) + response.Chain = append(response.Chain, convertCertificateToResponse(hostname, cert)) } } return response, nil } -func convertCertificateToResponse(cert *x509.Certificate) *clients.CertificateResponse { +func convertCertificateToResponse(hostname string, cert *x509.Certificate) *clients.CertificateResponse { response := &clients.CertificateResponse{ SubjectAN: cert.DNSNames, Emails: cert.EmailAddresses, @@ -175,6 +175,7 @@ func convertCertificateToResponse(cert *x509.Certificate) *clients.CertificateRe NotAfter: cert.NotAfter, Expired: clients.IsExpired(cert.NotAfter), SelfSigned: clients.IsSelfSigned(cert.AuthorityKeyId, cert.SubjectKeyId), + MisMatched: clients.IsMisMatchedCert(hostname, append(cert.DNSNames, cert.Subject.CommonName)), IssuerCN: cert.Issuer.CommonName, IssuerOrg: cert.Issuer.Organization, SubjectCN: cert.Subject.CommonName, diff --git a/pkg/tlsx/tlsx.go b/pkg/tlsx/tlsx.go index 0b5fdd51..d35c27c3 100644 --- a/pkg/tlsx/tlsx.go +++ b/pkg/tlsx/tlsx.go @@ -43,11 +43,16 @@ func New(options *clients.Options) (*Service, error) { // Connect connects to the input returning a response structure func (s *Service) Connect(host, port string) (*clients.Response, error) { - resp, err := s.client.Connect(host, port) + return s.ConnectWithOptions(host, port, clients.ConnectOptions{}) +} + +// Connect connects to the input with custom options +func (s *Service) ConnectWithOptions(host, port string, options clients.ConnectOptions) (*clients.Response, error) { + resp, err := s.client.ConnectWithOptions(host, port, options) if err != nil { wrappedErr := errors.Wrap(err, "could not connect to host") if s.options.ProbeStatus { - return &clients.Response{Host: host, Port: port, Error: err.Error(), ProbeStatus: false}, wrappedErr + return &clients.Response{Host: host, Port: port, Error: err.Error(), ProbeStatus: false, ServerName: options.SNI}, wrappedErr } return nil, wrappedErr } diff --git a/pkg/tlsx/ztls/ztls.go b/pkg/tlsx/ztls/ztls.go index 0c8771b7..ba58bb1e 100644 --- a/pkg/tlsx/ztls/ztls.go +++ b/pkg/tlsx/ztls/ztls.go @@ -14,6 +14,7 @@ import ( "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/iputil" "github.com/projectdiscovery/tlsx/pkg/tlsx/clients" + "github.com/rs/xid" "github.com/zmap/zcrypto/tls" "github.com/zmap/zcrypto/x509" ) @@ -54,9 +55,6 @@ func New(options *clients.Options) (*Client, error) { options: options, } - if options.ServerName != "" { - c.tlsConfig.ServerName = options.ServerName - } if len(options.Ciphers) > 0 { if customCiphers, err := toZTLSCiphers(options.Ciphers); err != nil { return nil, errors.Wrap(err, "could not get ztls ciphers") @@ -101,7 +99,7 @@ func (timeoutError) Timeout() bool { return true } func (timeoutError) Temporary() bool { return true } // Connect connects to a host and grabs the response data -func (c *Client) Connect(hostname, port string) (*clients.Response, error) { +func (c *Client) ConnectWithOptions(hostname, port string, options clients.ConnectOptions) (*clients.Response, error) { address := net.JoinHostPort(hostname, port) timeout := time.Duration(c.options.Timeout) * time.Second @@ -132,7 +130,14 @@ func (c *Client) Connect(hostname, port string) (*clients.Response, error) { config := c.tlsConfig if config.ServerName == "" { c := config.Clone() - c.ServerName = hostname + if options.SNI != "" { + c.ServerName = options.SNI + } else if iputil.IsIP(hostname) { + // using a random sni will return the default server certificate + c.ServerName = xid.New().String() + } else { + c.ServerName = hostname + } config = c } @@ -169,11 +174,12 @@ func (c *Client) Connect(hostname, port string) (*clients.Response, error) { Version: tlsVersion, Cipher: tlsCipher, TLSConnection: "ztls", - CertificateResponse: convertCertificateToResponse(parseSimpleTLSCertificate(hl.ServerCertificates.Certificate)), + CertificateResponse: convertCertificateToResponse(hostname, parseSimpleTLSCertificate(hl.ServerCertificates.Certificate)), + ServerName: config.ServerName, } if c.options.TLSChain { for _, cert := range hl.ServerCertificates.Chain { - response.Chain = append(response.Chain, convertCertificateToResponse(parseSimpleTLSCertificate(cert))) + response.Chain = append(response.Chain, convertCertificateToResponse(hostname, parseSimpleTLSCertificate(cert))) } } return response, nil @@ -184,7 +190,7 @@ func parseSimpleTLSCertificate(cert tls.SimpleCertificate) *x509.Certificate { return parsed } -func convertCertificateToResponse(cert *x509.Certificate) *clients.CertificateResponse { +func convertCertificateToResponse(hostname string, cert *x509.Certificate) *clients.CertificateResponse { if cert == nil { return nil } @@ -195,6 +201,7 @@ func convertCertificateToResponse(cert *x509.Certificate) *clients.CertificateRe NotAfter: cert.NotAfter, Expired: clients.IsExpired(cert.NotAfter), SelfSigned: clients.IsSelfSigned(cert.AuthorityKeyId, cert.SubjectKeyId), + MisMatched: clients.IsMisMatchedCert(hostname, append(cert.DNSNames, cert.Subject.CommonName)), IssuerDN: cert.Issuer.String(), IssuerCN: cert.Issuer.CommonName, IssuerOrg: cert.Issuer.Organization,