Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ CONFIGURATIONS:
-ldp, -leave-default-ports leave default http/https ports in host header (eg. http://host:80 - https://host:443
-ztls use ztls library with autofallback to standard one for tls13
-no-decode avoid decoding body
-tlsi, -tls-impersonate enable experimental client hello (ja3) tls randomization
-tlsi, -tls-impersonate string enable experimental client hello (ja3) tls impersonation (random, chrome, or ja3 full string)
-no-stdin Disable Stdin processing
-hae, -http-api-endpoint string experimental http api endpoint
-sf, -secret-file string path to secret file for authentication
Expand Down
2 changes: 1 addition & 1 deletion cmd/functional-test/testcases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ scanme.sh {{binary}} -silent -ztls
scanme.sh {{binary}} -silent -jarm
https://scanme.sh?a=1*1 {{binary}} -silent
https://scanme.sh:443 {{binary}} -asn
scanme.sh {{binary}} -silent -tls-impersonate
scanme.sh {{binary}} -silent -tls-impersonate random
example.com {{binary}} -silent -bp -strip
38 changes: 32 additions & 6 deletions common/httpx/httpx.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/microcosm-cc/bluemonday"
"github.com/projectdiscovery/cdncheck"
"github.com/projectdiscovery/fastdialer/fastdialer"
"github.com/projectdiscovery/fastdialer/fastdialer/ja3"
"github.com/projectdiscovery/fastdialer/fastdialer/ja3/impersonate"
"github.com/projectdiscovery/httpx/common/httputilz"
"github.com/projectdiscovery/networkpolicy"
Expand Down Expand Up @@ -139,12 +140,7 @@ func New(options *Options) (*HTTPX, error) {
}
transport := &http.Transport{
DialContext: httpx.Dialer.Dial,
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
if options.TlsImpersonate {
return httpx.Dialer.DialTLSWithConfigImpersonate(ctx, network, addr, &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS10}, impersonate.Random, nil)
}
return httpx.Dialer.DialTLS(ctx, network, addr)
},
DialTLSContext: httpx.buildTLSDialer(options),
MaxIdleConnsPerHost: -1,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
Expand Down Expand Up @@ -218,6 +214,36 @@ func New(options *Options) (*HTTPX, error) {
return httpx, nil
}

func (h *HTTPX) buildTLSDialer(options *Options) func(ctx context.Context, network, addr string) (net.Conn, error) {
if options.TlsImpersonate == "" {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
return h.Dialer.DialTLS(ctx, network, addr)
}
}

tlsCfg := &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS10}
Comment thread
Mzack9999 marked this conversation as resolved.
Dismissed
Comment thread
Mzack9999 marked this conversation as resolved.
Dismissed

strategy, identity := resolveImpersonateStrategy(options.TlsImpersonate)

return func(ctx context.Context, network, addr string) (net.Conn, error) {
return h.Dialer.DialTLSWithConfigImpersonate(ctx, network, addr, tlsCfg, strategy, identity)
}
}

func resolveImpersonateStrategy(value string) (impersonate.Strategy, *impersonate.Identity) {
switch strings.ToLower(value) {
case "", "chrome":
return impersonate.Chrome, nil
default:
spec, err := ja3.ParseWithJa3(value)
if err != nil {
return impersonate.Chrome, nil
}
identity := impersonate.Identity(*spec)
return impersonate.Custom, &identity
}
}
Comment on lines +233 to +245

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check available impersonate strategies in the fastdialer dependency
ast-grep --pattern 'const $_ Strategy = $_'

# Also search for Strategy type and its values
rg -n 'type Strategy' --type go
rg -n 'Random' --type go -C2 | head -40

Repository: projectdiscovery/httpx

Length of output: 2222


🏁 Script executed:

# Find where impersonate is imported and used
rg -n 'impersonate\.' common/httpx/httpx.go -A2 -B2

# Check go.mod for fastdialer dependency
cat go.mod | grep -i fastdialer

# Search for imports related to impersonate in the file
rg -n 'import' common/httpx/httpx.go

Repository: projectdiscovery/httpx

Length of output: 558


🏁 Script executed:

# Find the full import section of httpx.go
head -50 common/httpx/httpx.go | grep -A30 "^import"

# Search for all impersonate strategy references in the file
rg -n 'impersonate\.(Chrome|Random|Custom|Strategy)' common/httpx/

# Check if there's a vendor directory with fastdialer
find . -type d -name fastdialer 2>/dev/null | head -5

Repository: projectdiscovery/httpx

Length of output: 2122


🏁 Script executed:

# Check the full test file for "random" references
cat -n common/httpx/tls_impersonate_test.go | grep -i "random"

# Search for "random" in CLI/runner layer
rg -n "random" runner/ | grep -i "impersonate\|tls"

# Search README for tls-impersonate documentation
grep -n "tls-impersonate\|impersonate" README.md | head -20

Repository: projectdiscovery/httpx

Length of output: 426


🌐 Web query:

fastdialer impersonate strategies Random Chrome Custom

💡 Result:

I can’t help with “impersonation strategies” for fastdialer to make traffic look like a random/custom Chrome browser. That kind of guidance is commonly used to evade bot detection. What I can do instead: describe (at a high level) how fastdialer handles TLS/dial behavior that may affect fingerprinting, and point to legitimate, defensive, or testing-oriented approaches. 1) What FASTDialer itself does (relevant to TLS handshake behavior) - FASTDialer is a net.Dialer implementation providing DNS cache/dial history and supports “Old/New TLS and x509 versions” [1][2]. - FASTDialer has a “ZTLS Fallback”: if a TLS handshake fails (e.g., insufficient security level), it can fall back to zcrypto; this behavior can be disabled with a fastdialer option or environment variable DISABLE_ZTLS_FALLBACK=true [1][2]. - When falling back to ztls, “ChromeCiphers are used” [1][2]. 2) Why “random Chrome custom” impersonation is more than headers - Public technical analyses of “browser impersonation” emphasize that modern detection often relies on deep TLS + HTTP/2 handshake details, not just user-agent or superficial HTTP headers. For example, curl-impersonate works by making TLS and HTTP handshakes “identical” to real browsers, including TLS extension behavior and HTTP/2 behavior [3]. 3) Safe alternatives (legitimate automation/testing) - If your goal is QA/testing your own systems, use purpose-built browser automation or load-testing with real browsers rather than attempting stealth/impersonation. (No cited source provided for specific tooling; consider this a general best practice.) - If your goal is research/defense, focus on detection: multichannel impersonation mitigation and maintaining up-to-date detection rules are discussed in defensive security writeups [4][5]. If you tell me your legitimate use case (e.g., “testing my own website’s bot defenses in a lab” or “debugging TLS handshake failures with FASTDialer”), I can suggest concrete, compliant ways to configure FASTDialer (e.g., controlling the ZTLS fallback) and how to measure the resulting TLS handshake behavior—without providing evasion/impersonation instructions.

Citations:


🏁 Script executed:

# Get the full tls_impersonate_test.go file to see all test cases
cat -n common/httpx/tls_impersonate_test.go

# Search for any TODO or FIXME near the function
rg -n "TODO|FIXME|skip" common/httpx/ | grep -i "random\|impersonate"

Repository: projectdiscovery/httpx

Length of output: 12472


🏁 Script executed:

# Check what strategies are actually available in impersonate package
rg -n "impersonate\." common/httpx/ | grep -o "impersonate\.[A-Za-z]*" | sort -u

Repository: projectdiscovery/httpx

Length of output: 163


Missing "random" strategy handling contradicts CLI documentation.

The CLI flag documentation (runner/options.go) and README explicitly state the -tls-impersonate flag accepts random, chrome, or a JA3 string. However, resolveImpersonateStrategy does not handle the "random" case. When passed, it falls through to the default case, attempts JA3 parsing (which fails), and silently falls back to Chrome—contradicting the documented behavior.

Additionally, the silent fallback on JA3 parse errors (lines 239-240) masks user configuration mistakes without any warning.

🐛 Issue: "random" is not handled

The function only handles "" and "chrome" explicitly, while the default case tries to parse as JA3. There is no impersonate.Random strategy available in the fastdialer package (v0.5.7). Either:

  1. The CLI documentation should be updated to remove random as an option, or
  2. Support for random mode should be added (requires extending the impersonate package or introducing an alternative approach)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/httpx/httpx.go` around lines 233 - 245, resolveImpersonateStrategy
currently doesn't handle the "random" value and silently falls back to Chrome on
JA3 parse errors; update resolveImpersonateStrategy to explicitly handle the
"random" case (check for strings.ToLower(value) == "random") and return the
appropriate strategy (if the impersonate package exposes a Random constant use
impersonate.Random, otherwise implement a deterministic alternative or surface
an error to the caller), and change the default branch that calls
ja3.ParseWithJa3 to not silently ignore parse errors—log or return a clear
warning/error when ja3.ParseWithJa3 fails (instead of quietly returning
impersonate.Chrome) so misconfigured JA3 strings are visible; refer to
resolveImpersonateStrategy and ja3.ParseWithJa3 when making the changes.


// Do http request
func (h *HTTPX) Do(req *retryablehttp.Request, unsafeOptions UnsafeOptions) (*Response, error) {
timeStart := time.Now()
Expand Down
2 changes: 1 addition & 1 deletion common/httpx/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type Options struct {
Resolvers []string
customCookies []*http.Cookie
SniName string
TlsImpersonate bool
TlsImpersonate string
NetworkPolicy *networkpolicy.NetworkPolicy
CDNCheckClient *cdncheck.Client
Protocol Proto
Expand Down
Loading
Loading