diff --git a/README.md b/README.md index 32ac6521..12457a70 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,7 @@ CONFIGURATIONS: -rsr, -response-size-read int max response size to read in bytes (default 10485760) -rss, -response-size-save int max response size to read in bytes (default 1048576) -reset reset removes all nuclei configuration and data files (including nuclei-templates) + -tlsi, -tls-impersonate enable experimental client hello (ja3) tls randomization INTERACTSH: -iserver, -interactsh-server string interactsh server url for self-hosted instance (default: oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me) diff --git a/v2/cmd/functional-test/testcases.txt b/v2/cmd/functional-test/testcases.txt index 697486b0..1fa04148 100644 --- a/v2/cmd/functional-test/testcases.txt +++ b/v2/cmd/functional-test/testcases.txt @@ -89,3 +89,7 @@ {{binary}} -id tls-version -u scanme.sh # host:port {{binary}} -id tls-version -u scanme.sh:22 + +# Options +# Tls Impersonate +{{binary}} -id tech-detect -tlsi -u https://scanme.sh \ No newline at end of file diff --git a/v2/cmd/nuclei/main.go b/v2/cmd/nuclei/main.go index 60318774..28d1d534 100644 --- a/v2/cmd/nuclei/main.go +++ b/v2/cmd/nuclei/main.go @@ -210,6 +210,7 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.IntVarP(&options.ResponseReadSize, "response-size-read", "rsr", 10*1024*1024, "max response size to read in bytes"), flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", 1*1024*1024, "max response size to read in bytes"), flagSet.CallbackVar(resetCallback, "reset", "reset removes all nuclei configuration and data files (including nuclei-templates)"), + flagSet.BoolVarP(&options.TlsImpersonate, "tls-impersonate", "tlsi", false, "enable experimental client hello (ja3) tls randomization"), ) flagSet.CreateGroup("interactsh", "interactsh", diff --git a/v2/cmd/nuclei/test.yaml b/v2/cmd/nuclei/test.yaml new file mode 100644 index 00000000..ecb62d2e --- /dev/null +++ b/v2/cmd/nuclei/test.yaml @@ -0,0 +1,18 @@ +id: basic-example + +info: + name: Test HTTP Template + author: pdteam + severity: info + +http: + - raw: + - |+ + GET / HTTP/1.1 + Host: {{Hostname}} + + unsafe: true + matchers: + - type: dsl + dsl: + - true \ No newline at end of file diff --git a/v2/go.mod b/v2/go.mod index 4b13cce8..89369095 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -21,11 +21,11 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/pkg/errors v0.9.1 github.com/projectdiscovery/clistats v0.0.12 - github.com/projectdiscovery/fastdialer v0.0.29 + github.com/projectdiscovery/fastdialer v0.0.31 github.com/projectdiscovery/hmap v0.0.13 github.com/projectdiscovery/interactsh v1.1.4 github.com/projectdiscovery/rawhttp v0.1.13 - github.com/projectdiscovery/retryabledns v1.0.29 + github.com/projectdiscovery/retryabledns v1.0.30 github.com/projectdiscovery/retryablehttp-go v1.0.17 github.com/projectdiscovery/yamldoc-go v1.0.4 github.com/remeh/sizedwaitgroup v1.0.0 diff --git a/v2/go.sum b/v2/go.sum index 41edc78b..c38b6324 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -411,8 +411,8 @@ github.com/projectdiscovery/clistats v0.0.12 h1:KLYJxpiwEFidduU4PbcwEcCQ2L7c5wrf github.com/projectdiscovery/clistats v0.0.12/go.mod h1:9luKJj+7Hjq3+a7g129sKWRYx4SbTdkUWZQxabn3H5Y= github.com/projectdiscovery/dsl v0.0.9 h1:VfznBxpbNKMn2amQd9gtRnMfK1/Sf9MwsJD9x2Et/fY= github.com/projectdiscovery/dsl v0.0.9/go.mod h1:kdPdbbqceWxkSedXm99z0Hzh9z/DFj42A9L95GJjybo= -github.com/projectdiscovery/fastdialer v0.0.29 h1:uDy2/bXHl8ISkuRp0EpmajkfWHewL3q5oDcYxB07ME8= -github.com/projectdiscovery/fastdialer v0.0.29/go.mod h1:CBzmr7QS+Ml66h1jjuudR8Uzl6bt2YeqYmTg0IedWsI= +github.com/projectdiscovery/fastdialer v0.0.31 h1:eu0wTBCWjT8dXChmBtnQaAxoFpkLdvq0VroRxZoe/M8= +github.com/projectdiscovery/fastdialer v0.0.31/go.mod h1:ttLvt0xnpNQAStYYQ6ElIBHfSXHuPEiXBkLH/OLbYlc= github.com/projectdiscovery/fasttemplate v0.0.2 h1:h2cISk5xDhlJEinlBQS6RRx0vOlOirB2y3Yu4PJzpiA= github.com/projectdiscovery/fasttemplate v0.0.2/go.mod h1:XYWWVMxnItd+r0GbjA1GCsUopMw1/XusuQxdyAIHMCw= github.com/projectdiscovery/freeport v0.0.4 h1:H4VrK/7hUcC1zbg46zv9iSMBACBDpUqcHkV+FUyXISw= @@ -439,8 +439,8 @@ github.com/projectdiscovery/rawhttp v0.1.13 h1:Xn3NY3SYIk0151K5Qfuvx3tayl2UOoxMu github.com/projectdiscovery/rawhttp v0.1.13/go.mod h1:AjZUYdPCx4xqeWYPqFPLGCxQsVFeUrobxidnU6Nta8M= github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917 h1:m03X4gBVSorSzvmm0bFa7gDV4QNSOWPL/fgZ4kTXBxk= github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917/go.mod h1:JxXtZC9e195awe7EynrcnBJmFoad/BNDzW9mzFkK8Sg= -github.com/projectdiscovery/retryabledns v1.0.29 h1:44EphLP5gRgVxlge9/qm5Gue+9cDd/BAILTF9PQQx54= -github.com/projectdiscovery/retryabledns v1.0.29/go.mod h1:NtbDTfcsW9hIUf0HuVQNZSTTG063Phy0uaBBjZlif0Q= +github.com/projectdiscovery/retryabledns v1.0.30 h1:7bc8Lq3r/qzw4LdXXAxKtQa52iGiEx1WasZLVCO6Oj0= +github.com/projectdiscovery/retryabledns v1.0.30/go.mod h1:+Aqc0TjKGcTtP0HtXE8o1GzrjAHhSno6hSF+L63TBtI= github.com/projectdiscovery/retryablehttp-go v1.0.17 h1:oppnrypatWsHxcMU5RuAcUsUu3nxBhId2CF3OBj9XJA= github.com/projectdiscovery/retryablehttp-go v1.0.17/go.mod h1:zJh8bQdxhIsaEGnxsacvMbgiCKT4UAOr4T1kZBnSa68= github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us= diff --git a/v2/pkg/protocols/headless/engine/http_client.go b/v2/pkg/protocols/headless/engine/http_client.go index 50f97c7d..1195a054 100644 --- a/v2/pkg/protocols/headless/engine/http_client.go +++ b/v2/pkg/protocols/headless/engine/http_client.go @@ -11,6 +11,7 @@ import ( "golang.org/x/net/proxy" + "github.com/projectdiscovery/fastdialer/fastdialer/ja3/impersonate" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils" "github.com/projectdiscovery/nuclei/v2/pkg/types" @@ -39,9 +40,14 @@ func newHttpClient(options *types.Options) (*http.Client, error) { } transport := &http.Transport{ - ForceAttemptHTTP2: options.ForceAttemptHTTP2, - DialContext: dialer.Dial, - DialTLSContext: dialer.DialTLS, + ForceAttemptHTTP2: options.ForceAttemptHTTP2, + DialContext: dialer.Dial, + DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + if options.TlsImpersonate { + return dialer.DialTLSWithConfigImpersonate(ctx, network, addr, tlsConfig, impersonate.Random, nil) + } + return dialer.DialTLS(ctx, network, addr) + }, MaxIdleConns: 500, MaxIdleConnsPerHost: 500, MaxConnsPerHost: 500, diff --git a/v2/pkg/protocols/http/httpclientpool/clientpool.go b/v2/pkg/protocols/http/httpclientpool/clientpool.go index c972616b..ececb083 100644 --- a/v2/pkg/protocols/http/httpclientpool/clientpool.go +++ b/v2/pkg/protocols/http/httpclientpool/clientpool.go @@ -17,6 +17,7 @@ import ( "golang.org/x/net/publicsuffix" "github.com/projectdiscovery/fastdialer/fastdialer" + "github.com/projectdiscovery/fastdialer/fastdialer/ja3/impersonate" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils" "github.com/projectdiscovery/nuclei/v2/pkg/types" @@ -225,9 +226,14 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl } transport := &http.Transport{ - ForceAttemptHTTP2: options.ForceAttemptHTTP2, - DialContext: Dialer.Dial, - DialTLSContext: Dialer.DialTLS, + ForceAttemptHTTP2: options.ForceAttemptHTTP2, + DialContext: Dialer.Dial, + DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + if options.TlsImpersonate { + return Dialer.DialTLSWithConfigImpersonate(ctx, network, addr, tlsConfig, impersonate.Random, nil) + } + return Dialer.DialTLS(ctx, network, addr) + }, MaxIdleConns: maxIdleConns, MaxIdleConnsPerHost: maxIdleConnsPerHost, MaxConnsPerHost: maxConnsPerHost, @@ -244,6 +250,7 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl if proxyErr != nil { return nil, proxyErr } + dialer, err := proxy.FromURL(socksURL, proxy.Direct) if err != nil { return nil, err @@ -252,16 +259,15 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl dc := dialer.(interface { DialContext(ctx context.Context, network, addr string) (net.Conn, error) }) - if proxyErr == nil { - transport.DialContext = dc.DialContext - transport.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { - // upgrade proxy connection to tls - conn, err := dc.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - return tls.Client(conn, tlsConfig), nil + + transport.DialContext = dc.DialContext + transport.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + // upgrade proxy connection to tls + conn, err := dc.DialContext(ctx, network, addr) + if err != nil { + return nil, err } + return tls.Client(conn, tlsConfig), nil } } diff --git a/v2/pkg/types/types.go b/v2/pkg/types/types.go index f131df57..0e9bacf6 100644 --- a/v2/pkg/types/types.go +++ b/v2/pkg/types/types.go @@ -360,6 +360,8 @@ type Options struct { FuzzingType string // Fuzzing Mode overrides template level fuzzing-mode configuration FuzzingMode string + // TlsImpersonate enables TLS impersonation + TlsImpersonate bool } // ShouldLoadResume resume file