Skip to content

Commit 2ee81a3

Browse files
authored
Merge pull request #59 from projectdiscovery/dev
v0.0.5 Release
2 parents 2c94fc1 + 0747cff commit 2ee81a3

File tree

11 files changed

+271
-14
lines changed

11 files changed

+271
-14
lines changed

README.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ A fast and configurable TLS grabber focused on TLS based **data collection and a
3434
- **Auto TLS Fallback** for older TLS version
3535
- **Pre Handshake** TLS connection (early termination)
3636
- Customizable **Cipher / SNI / TLS** selection
37+
- **JARM/JA3** TLS Fingerprint
3738
- **TLS Misconfigurations**
3839
- **HOST, IP, URL** and **CIDR** input
3940
- STD **IN/OUT** and **TXT/JSON** output
@@ -77,6 +78,7 @@ PROBES:
7778
-cipher display used cipher
7879
-hash string display certificate fingerprint hashes (md5,sha1,sha256)
7980
-jarm display jarm fingerprint hash
81+
-ja3 display ja3 fingerprint hash (using ztls)
8082
-tps, -probe-status display tls probe status
8183

8284
MISCONFIGURATIONS:
@@ -89,15 +91,18 @@ CONFIGURATIONS:
8991
-r, -resolvers string[] list of resolvers to use
9092
-cc, -cacert string client certificate authority file
9193
-ci, -cipher-input string[] ciphers to use with tls connection
92-
-sni string tls sni hostname to use
94+
-sni string[] tls sni hostname to use
9395
-min-version string minimum tls version to accept (ssl30,tls10,tls11,tls12,tls13)
9496
-max-version string maximum tls version to accept (ssl30,tls10,tls11,tls12,tls13)
95-
-tc, -tls-chain display tls chain in json output
97+
-ac, -all-ciphers send all ciphers as accepted inputs
98+
-cert, -certificate include certificates in json output (PEM format)
99+
-tc, -tls-chain include certificates chain in json output
96100
-vc, -verify-cert enable verification of server certificate
97101

98102
OPTIMIZATIONS:
99103
-c, -concurrency int number of concurrent threads to process (default 300)
100104
-timeout int tls connection timeout in seconds (default 5)
105+
-retries int number of retries to perform for failures (default 3)
101106

102107
OUTPUT:
103108
-o, -output string file to write output to
@@ -316,6 +321,22 @@ self-signed.badssl.com:443 [self-signed]
316321
expired.badssl.com:443 [expired]
317322
```
318323

324+
### [JARM](https://engineering.salesforce.com/easily-identify-malicious-servers-on-the-internet-with-jarm-e095edac525a/) TLS Fingerprint
325+
326+
```console
327+
$ echo hackerone.com | tlsx -jarm -silent
328+
329+
hackerone.com:443 [29d3dd00029d29d00042d43d00041d5de67cc9954cc85372523050f20b5007]
330+
```
331+
332+
### [JA3](https://github.com/salesforce/ja3) TLS Fingerprint
333+
334+
```console
335+
$ echo hackerone.com | tlsx -ja3 -silent
336+
337+
hackerone.com:443 [20c9baf81bfe96ff89722899e75d0190]
338+
```
339+
319340
### JSON Output
320341

321342
**tlsx** does support multiple probe flags to query specific data, but all the information is always available in JSON format, for automation and post processing using `-json` output is most convenient option to use.

cmd/tlsx/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ func readFlags() error {
6262
flagSet.BoolVar(&options.Cipher, "cipher", false, "display used cipher"),
6363
flagSet.StringVar(&options.Hash, "hash", "", "display certificate fingerprint hashes (md5,sha1,sha256)"),
6464
flagSet.BoolVar(&options.Jarm, "jarm", false, "display jarm fingerprint hash"),
65+
flagSet.BoolVar(&options.Ja3, "ja3", false, "display ja3 fingerprint hash (using ztls)"),
6566
flagSet.BoolVarP(&options.ProbeStatus, "probe-status", "tps", false, "display tls probe status"),
6667
)
6768

@@ -79,13 +80,16 @@ func readFlags() error {
7980
flagSet.StringSliceVar(&options.ServerName, "sni", nil, "tls sni hostname to use", goflags.FileCommaSeparatedStringSliceOptions),
8081
flagSet.StringVar(&options.MinVersion, "min-version", "", "minimum tls version to accept (ssl30,tls10,tls11,tls12,tls13)"),
8182
flagSet.StringVar(&options.MaxVersion, "max-version", "", "maximum tls version to accept (ssl30,tls10,tls11,tls12,tls13)"),
82-
flagSet.BoolVarP(&options.TLSChain, "tls-chain", "tc", false, "display tls chain in json output"),
83+
flagSet.BoolVarP(&options.AllCiphers, "all-ciphers", "ac", false, "send all ciphers as accepted inputs"),
84+
flagSet.BoolVarP(&options.Cert, "certificate", "cert", false, "include certificates in json output (PEM format)"),
85+
flagSet.BoolVarP(&options.TLSChain, "tls-chain", "tc", false, "include certificates chain in json output"),
8386
flagSet.BoolVarP(&options.VerifyServerCertificate, "verify-cert", "vc", false, "enable verification of server certificate"),
8487
)
8588

8689
flagSet.CreateGroup("optimizations", "Optimizations",
8790
flagSet.IntVarP(&options.Concurrency, "concurrency", "c", 300, "number of concurrent threads to process"),
8891
flagSet.IntVar(&options.Timeout, "timeout", 5, "tls connection timeout in seconds"),
92+
flagSet.IntVar(&options.Retries, "retries", 3, "number of retries to perform for failures"),
8993
)
9094

9195
flagSet.CreateGroup("output", "Output",

internal/runner/banner.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ var banner = fmt.Sprintf(`
1717
|_| |____|___/_/\_\ %s
1818
`, version)
1919

20-
var version = "v0.0.4"
20+
var version = "v0.0.5"
2121

2222
// validateOptions validates the provided options for crawler
2323
func (r *Runner) validateOptions() error {
2424
r.hasStdin = fileutil.HasStdin()
2525

26+
if r.options.Retries == 0 {
27+
r.options.Retries = 1
28+
}
2629
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
2730
if r.options.RespOnly && probeSpecified {
2831
return errors.New("resp-only flag can only be used with san and cn flags")
@@ -40,7 +43,7 @@ func (r *Runner) validateOptions() error {
4043
if r.options.CertsOnly && !(r.options.ScanMode == "ztls" || r.options.ScanMode == "auto") {
4144
return errors.New("scan-mode must be ztls or auto with certs-only option")
4245
}
43-
if r.options.CertsOnly {
46+
if r.options.CertsOnly || r.options.Ja3 {
4447
r.options.ScanMode = "ztls" // force setting ztls when using certs-only
4548
}
4649
if r.options.Verbose {

pkg/output/output.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func (w *StandardWriter) formatStandard(output *clients.Response) ([]byte, error
151151
builder.WriteString(w.aurora.Blue(output.ServerName).String())
152152
builder.WriteString("]")
153153
}
154-
if w.options.SO && len(cert.SubjectOrg) > 0 {
154+
if w.options.SO && len(cert.SubjectOrg) > 0 {
155155
builder.WriteString(" [")
156156
builder.WriteString(w.aurora.BrightYellow(strings.Join(cert.SubjectOrg, ",")).String())
157157
builder.WriteString("]")
@@ -205,6 +205,12 @@ func (w *StandardWriter) formatStandard(output *clients.Response) ([]byte, error
205205
builder.WriteString("]")
206206
}
207207

208+
if w.options.Ja3 && output.Ja3Hash != "" {
209+
builder.WriteString(" [")
210+
builder.WriteString(w.aurora.Magenta(output.Ja3Hash).String())
211+
builder.WriteString("]")
212+
}
213+
208214
outputdata := builder.Bytes()
209215
return outputdata, nil
210216
}

pkg/tlsx/clients/clients.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"crypto/sha1"
77
"crypto/sha256"
88
"encoding/hex"
9+
"encoding/pem"
910
"math"
1011
"strings"
1112
"time"
@@ -39,6 +40,8 @@ type Options struct {
3940
JSON bool
4041
// TLSChain enables printing TLS chain information to output
4142
TLSChain bool
43+
// AllCiphers enables sending all ciphers as client
44+
AllCiphers bool
4245
// ProbeStatus enables writing of errors with json output
4346
ProbeStatus bool
4447
// CertsOnly enables early SSL termination using ztls flag
@@ -49,6 +52,8 @@ type Options struct {
4952
Silent bool
5053
// NoColor disables coloring of CLI output
5154
NoColor bool
55+
// Retries is the number of times to retry TLS connection
56+
Retries int
5257
// Timeout is the number of seconds to wait for connection
5358
Timeout int
5459
// Concurrency is the number of concurrent threads to process
@@ -92,6 +97,10 @@ type Options struct {
9297
Hash string
9398
// Jarm calculate jarm fingerprinting with multiple probes
9499
Jarm bool
100+
// Cert displays certificate in pem format
101+
Cert bool
102+
// Ja3 displays ja3 fingerprint hash
103+
Ja3 bool
95104

96105
// Fastdialer is a fastdialer dialer instance
97106
Fastdialer *fastdialer.Dialer
@@ -124,6 +133,7 @@ type Response struct {
124133
// Chain is the chain of certificates
125134
Chain []*CertificateResponse `json:"chain,omitempty"`
126135
JarmHash string `json:"jarm_hash,omitempty"`
136+
Ja3Hash string `json:"ja3_hash,omitempty"`
127137
ServerName string `json:"sni,omitempty"`
128138
}
129139

@@ -157,6 +167,8 @@ type CertificateResponse struct {
157167
Emails []string `json:"emails,omitempty"`
158168
// FingerprintHash is the hashes for certificate
159169
FingerprintHash CertificateResponseFingerprintHash `json:"fingerprint_hash,omitempty"`
170+
// Certificate is the raw certificate in PEM format
171+
Certificate string `json:"certificate,omitempty"`
160172
}
161173

162174
// CertificateDistinguishedName is a distinguished certificate name
@@ -247,6 +259,15 @@ func IsMisMatchedCert(host string, names []string) bool {
247259
return true
248260
}
249261

262+
// PemEncode encodes a raw certificate to PEM format.
263+
func PemEncode(cert []byte) string {
264+
var buf bytes.Buffer
265+
if err := pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert}); err != nil {
266+
return ""
267+
}
268+
return buf.String()
269+
}
270+
250271
type ConnectOptions struct {
251272
SNI string
252273
}

pkg/tlsx/tls/tls.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ func New(options *clients.Options) (*Client, error) {
5757
options: options,
5858
}
5959

60+
if options.AllCiphers {
61+
c.tlsConfig.CipherSuites = allCiphers
62+
}
6063
if len(options.Ciphers) > 0 {
6164
if customCiphers, err := toTLSCiphers(options.Ciphers); err != nil {
6265
return nil, errors.Wrap(err, "could not get tls ciphers")
@@ -156,18 +159,18 @@ func (c *Client) ConnectWithOptions(hostname, port string, options clients.Conne
156159
Version: tlsVersion,
157160
Cipher: tlsCipher,
158161
TLSConnection: "ctls",
159-
CertificateResponse: convertCertificateToResponse(hostname, leafCertificate),
162+
CertificateResponse: c.convertCertificateToResponse(hostname, leafCertificate),
160163
ServerName: config.ServerName,
161164
}
162165
if c.options.TLSChain {
163166
for _, cert := range certificateChain {
164-
response.Chain = append(response.Chain, convertCertificateToResponse(hostname, cert))
167+
response.Chain = append(response.Chain, c.convertCertificateToResponse(hostname, cert))
165168
}
166169
}
167170
return response, nil
168171
}
169172

170-
func convertCertificateToResponse(hostname string, cert *x509.Certificate) *clients.CertificateResponse {
173+
func (c *Client) convertCertificateToResponse(hostname string, cert *x509.Certificate) *clients.CertificateResponse {
171174
response := &clients.CertificateResponse{
172175
SubjectAN: cert.DNSNames,
173176
Emails: cert.EmailAddresses,
@@ -196,6 +199,9 @@ func convertCertificateToResponse(hostname string, cert *x509.Certificate) *clie
196199
} else {
197200
response.SubjectDN = cert.Subject.String()
198201
}
202+
if c.options.Cert {
203+
response.Certificate = clients.PemEncode(cert.Raw)
204+
}
199205
return response
200206
}
201207

pkg/tlsx/tls/utils.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import (
55
"fmt"
66
)
77

8+
var allCiphers []uint16
9+
10+
func init() {
11+
for _, cipher := range tlsCiphers {
12+
allCiphers = append(allCiphers, cipher)
13+
}
14+
}
15+
816
func toTLSCiphers(items []string) ([]uint16, error) {
917
var convertedCiphers []uint16
1018
for _, item := range items {

pkg/tlsx/tlsx.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,26 @@ func (s *Service) Connect(host, port string) (*clients.Response, error) {
4848

4949
// Connect connects to the input with custom options
5050
func (s *Service) ConnectWithOptions(host, port string, options clients.ConnectOptions) (*clients.Response, error) {
51-
resp, err := s.client.ConnectWithOptions(host, port, options)
51+
var resp *clients.Response
52+
var err error
53+
54+
for i := 0; i < s.options.Retries; i++ {
55+
if resp, err = s.client.ConnectWithOptions(host, port, options); resp != nil {
56+
err = nil
57+
break
58+
}
59+
}
60+
if resp == nil && err == nil {
61+
return nil, errors.New("no response returned for connection")
62+
}
5263
if err != nil {
5364
wrappedErr := errors.Wrap(err, "could not connect to host")
5465
if s.options.ProbeStatus {
5566
return &clients.Response{Host: host, Port: port, Error: err.Error(), ProbeStatus: false, ServerName: options.SNI}, wrappedErr
5667
}
5768
return nil, wrappedErr
5869
}
70+
5971
if s.options.Jarm {
6072
port, _ := strconv.Atoi(port)
6173
timeout := time.Duration(s.options.Timeout) * time.Second

0 commit comments

Comments
 (0)