projectdiscovery/nuclei · error
smb connect: %w
Error message
smb connect: %w
What it means
The DCERPC/SMB JS client could not establish its SMB session: connect() first verifies the host against protocolstate's network policy (a denied host fails earlier with ErrHostDenied, not this), then dials via goimpacket's SMB client bound to an execution-scoped dialer; any failure of smb.Connect() is wrapped as 'smb connect: ...'. The %w preserves goimpacket's error: unreachable host, auth failure, or protocol/signing mismatch.
Source
Thrown at pkg/js/libs/dcerpc/dcerpc.go:164
func (c *Client) SetPort(port int) {
c.Port = port
c.target.Port = port
}
// connect lazily establishes the underlying SMB session that all RPC
// transports are tunneled through. The SMB Client is bound to a Dialer that
// captures this client's executionId so every dial inside goimpacket is
// validated against the same network policy.
func (c *Client) connect() error {
if c.started {
return nil
}
if !protocolstate.IsHostAllowed(c.nj.ExecutionId(), c.Host) {
return protocolstate.ErrHostDenied.Msgf(c.Host)
}
c.smb = gpsmb.NewClientWithDialer(c.target, c.creds, NewExecDialer(c.nj.ExecutionId()))
if err := c.smb.Connect(); err != nil {
return fmt.Errorf("smb connect: %w", err)
}
c.started = true
return nil
}
// Close releases the underlying SMB session.
func (c *Client) Close() {
if c.smb != nil {
c.smb.Close()
}
c.started = false
}
// rpcOverNamedPipe binds the supplied interface UUID over a named pipe and
// returns an authenticated *dcerpc.Client.
func (c *Client) rpcOverNamedPipe(pipe string, uuid [16]byte, major, minor uint16) (*gprpc.Client, error) {
if err := c.connect(); err != nil {
return nil, errView on GitHub (pinned to 265b3a3dec)
Solutions
- Verify reachability of 445: nc -vz host 445 (or test on 139 and configure accordingly)
- Correct the credential format — for domain accounts use DOMAIN\\user with lm/ntlm hashes where expected
- Confirm the SMB dialect: targets requiring SMB1 are unsupported by modern clients; enforce signing compatible settings
- Remember connect() is idempotent (c.started guard): a failed connect leaves started=false so you may retry after fixing the cause, but call Close() if you abandon the client
Example fix
// before
const c = new Client({ Host: '10.0.0.5', User: 'admin', Hash: '...' });
await c.connect(); // smb connect: ... (445 filtered)
// after
// firewall allows 445, domain-qualified user
const c = new Client({ Host: '10.0.0.5', User: 'CORP\\admin', Hash: 'aad3b435b51404eeaad3b435b51404ee:...' });
await c.connect(); Defensive patterns
Strategy: try-catch
Validate before calling
// in template JS: cheap reachability probe before SMB connect
const ok = await wireguard('tcp', Host, 445); // or a raw TCP dial helper if available
if (!ok) { log('445 closed — skip'); exit(); } Try / catch
Wrap the connect/usage sequence in try/catch; on 'smb connect:' inspect the wrapped goimpacket error — timeout/unreachable means network (skip host), STATUS_LOGON_FAILURE means credentials (fix creds), STATUS_ACCESS_DENIED means signing/permission config. Call Close() on failure so a later connect() may retry (started is reset).
Prevention
- Confirm tcp/445 (or 139) is reachable before running SMB templates
- Use DOMAIN\\user form and correct lm:ntlm hash format for domain accounts
- Keep hosts allowlisted in protocolstate policy; denied hosts fail earlier with ErrHostDenied
- Do not reuse one Client across targets — connect() is guarded by c.started per client
When it happens
Trigger: new dcerpc-style Client connect where tcp/445 is filtered or the service is down, credentials are wrong (NTLM hash/password/username), SMB signing is required but not negotiated, or the target speaks only SMB1.
Common situations: Templates binding to 445 against hosts exposing SMB only on 139; domain-credential formats wrong (user vs DOMAIN\user); firewalls dropping 445 mid-scan; NAT'd lab targets.
Related errors
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/958448ddaa74ce66.
Report an issue: GitHub.