-
Notifications
You must be signed in to change notification settings - Fork 149
fix: indefinite hangs during TLS handshake and cipher enumeration #960
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,7 +140,7 @@ func (c *Client) ConnectWithOptions(hostname, ip, port string, options clients.C | |
|
|
||
| // new tls connection | ||
| tlsConn := tls.Client(conn, config) | ||
| err = c.tlsHandshakeWithTimeout(tlsConn, ctx) | ||
| err = c.tlsHandshakeWithTimeout(ctx, tlsConn) | ||
| if err != nil { | ||
| if clients.IsClientCertRequiredError(err) { | ||
| clientCertRequired = true | ||
|
|
@@ -257,10 +257,18 @@ func (c *Client) EnumerateCiphers(hostname, ip, port string, options clients.Con | |
| conn := tls.Client(baseConn, baseCfg) | ||
| baseCfg.CipherSuites = []uint16{ztlsCiphers[v]} | ||
|
|
||
| if err := c.tlsHandshakeWithTimeout(conn, context.TODO()); err == nil { | ||
| ctx := context.Background() | ||
| var cancel context.CancelFunc | ||
| if c.options.Timeout != 0 { | ||
| ctx, cancel = context.WithTimeout(ctx, time.Duration(c.options.Timeout)*time.Second) | ||
| } | ||
| if err := c.tlsHandshakeWithTimeout(ctx, conn); err == nil { | ||
| h1 := conn.GetHandshakeLog() | ||
| enumeratedCiphers = append(enumeratedCiphers, h1.ServerHello.CipherSuite.String()) | ||
| } | ||
| if cancel != nil { | ||
| cancel() | ||
| } | ||
| _ = conn.Close() // also closes baseConn internally | ||
| } | ||
| return enumeratedCiphers, nil | ||
|
|
@@ -321,19 +329,20 @@ func (c *Client) getConfig(hostname, ip, port string, options clients.ConnectOpt | |
| } | ||
|
|
||
| // tlsHandshakeWithCtx attempts tls handshake with given timeout | ||
| func (c *Client) tlsHandshakeWithTimeout(tlsConn *tls.Conn, ctx context.Context) error { | ||
| func (c *Client) tlsHandshakeWithTimeout(ctx context.Context, tlsConn *tls.Conn) error { | ||
| errChan := make(chan error, 1) | ||
| defer close(errChan) | ||
|
|
||
| go func() { | ||
| errChan <- tlsConn.Handshake() | ||
| }() | ||
|
|
||
| select { | ||
| case <-ctx.Done(): | ||
| return errorutil.NewWithTag("ztls", "timeout while attempting handshake") //nolint | ||
| case errChan <- tlsConn.Handshake(): | ||
| } | ||
|
|
||
| err := <-errChan | ||
| if err == tls.ErrCertsOnly { | ||
| err = nil | ||
| case err := <-errChan: | ||
| if err == tls.ErrCertsOnly { | ||
| err = nil | ||
| } | ||
| return err | ||
| } | ||
|
Comment on lines
+332
to
347
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's read the file to see the code at lines 332-347 and surrounding context
wc -l pkg/tlsx/ztls/ztls.goRepository: projectdiscovery/tlsx Length of output: 90 🏁 Script executed: # Read the specific section mentioned in the review
sed -n '320,360p' pkg/tlsx/ztls/ztls.goRepository: projectdiscovery/tlsx Length of output: 816 🏁 Script executed: # Search for EnumerateCiphers function and how baseCfg.CipherSuites is used
rg -A 30 "func.*EnumerateCiphers" pkg/tlsx/ztls/Repository: projectdiscovery/tlsx Length of output: 1792 🏁 Script executed: # Search for how tlsHandshakeWithTimeout is called and how baseCfg is passed
rg -B 10 "tlsHandshakeWithTimeout" pkg/tlsx/ztls/Repository: projectdiscovery/tlsx Length of output: 1835 🏁 Script executed: # Get more context around the tlsHandshakeWithTimeout call in EnumerateCiphers
rg -B 5 -A 15 "conn := tls.Client.*baseConn.*baseCfg" pkg/tlsx/ztls/Repository: projectdiscovery/tlsx Length of output: 1274 🏁 Script executed: # Search for the loop that contains this code
rg -B 30 "baseCfg.CipherSuites = \[\]uint16" pkg/tlsx/ztls/Repository: projectdiscovery/tlsx Length of output: 1860 🏁 Script executed: # Check if there's any defer cancel() or cleanup that ensures goroutines are waited on
sed -n '140,280p' pkg/tlsx/ztls/ztls.goRepository: projectdiscovery/tlsx Length of output: 4787 🏁 Script executed: # Check for go.mod or go version requirements
cat go.mod | head -5Repository: projectdiscovery/tlsx Length of output: 126 🏁 Script executed: # Check tls.Config.Clone() documentation from Go standard library
web_search
Go tls.Config Clone() method standard libraryRepository: projectdiscovery/tlsx Length of output: 246 🌐 Web query:
💡 Result:
Clone the config to prevent race conditions in cipher enumeration. On timeout, Fix: Clone config for each attempt- conn := tls.Client(baseConn, baseCfg)
- baseCfg.CipherSuites = []uint16{ztlsCiphers[v]}
+ cfg := baseCfg.Clone()
+ cfg.CipherSuites = []uint16{ztlsCiphers[v]}
+ conn := tls.Client(baseConn, cfg)🤖 Prompt for AI Agents |
||
| return err | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: projectdiscovery/tlsx
Length of output: 203
🏁 Script executed:
Repository: projectdiscovery/tlsx
Length of output: 1252
🏁 Script executed:
Repository: projectdiscovery/tlsx
Length of output: 90
🏁 Script executed:
# Read the clients/utils.go file cat -n pkg/tlsx/clients/utils.goRepository: projectdiscovery/tlsx
Length of output: 5897
🏁 Script executed:
Repository: projectdiscovery/tlsx
Length of output: 2422
🏁 Script executed:
Repository: projectdiscovery/tlsx
Length of output: 73
🏁 Script executed:
Repository: projectdiscovery/tlsx
Length of output: 1067
🏁 Script executed:
Repository: projectdiscovery/tlsx
Length of output: 2120
Add default handshake timeout to cipher enumeration to prevent indefinite blocking.
pkg/tlsx/clients/utils.gotreatsTimeout == 0as a 5-second default viaGetConn(). However,EnumerateCiphersusespool.Acquire()directly, bypassing this default. Without a context deadline,conn.HandshakeContext()can hang indefinitely if a peer accepts TCP but never completes the TLS handshake.Proposed fix
🤖 Prompt for AI Agents