shadow1ng/fscan · error

failed to connect target: %s

Error message

failed to connect target: %s

What it means

smb2Grooms opens `grooms` parallel TCP connections to the target to spray SMB2 groom packets. The first net.Dial that fails aborts the whole loop, closes already-open connections, and returns 'failed to connect target'. This is the SMB2 stage's TCP connect failure.

Source

Thrown at plugins/services/ms17010_exp.go:922

func smb2Grooms(address string, grooms int) ([]net.Conn, error) {
	header := makeSMB2Header()
	var (
		conns []net.Conn
		ok    bool
	)
	defer func() {
		if ok {
			return
		}
		for i := 0; i < len(conns); i++ {
			_ = conns[i].Close()
		}
	}()
	for i := 0; i < grooms; i++ {
		conn, err := net.Dial("tcp", address)
		if err != nil {
			return nil, fmt.Errorf("failed to connect target: %s", err)
		}
		_, err = conn.Write(header)
		if err != nil {
			return nil, fmt.Errorf("failed to send SMB2 header: %s", err)
		}
		conns = append(conns, conn)
	}
	ok = true
	return conns, nil
}

func makeSMB2Header() []byte {
	buf := bytes.Buffer{}
	buf.Write([]byte{0x00, 0x00, 0xFF, 0xF7, 0xFE})
	buf.WriteString("SMB")
	buf.Write(makeZero(124))
	return buf.Bytes()
}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Reduce the grooms count (default is tuned; excessive values trigger failures)
  2. Verify port 445 reachability with a single connect test first
  3. Check firewall/rate-limiting on the target and intermediate devices
  4. Add backoff/retry on individual dial failures instead of aborting

Example fix

// before
conn, err := net.Dial("tcp", address)
if err != nil { return nil, fmt.Errorf("failed to connect target: %s", err) }
// after
conn, err := net.Dial("tcp", address)
if err != nil {
    time.Sleep(100 * time.Millisecond)
    conn, err = net.Dial("tcp", address)
    if err != nil { return nil, fmt.Errorf("failed to connect target: %s", err) }
}
Defensive patterns

Strategy: validation

Validate before calling

// before grooms, verify a single dial succeeds and tune the count
if err := canConnect(address); err != nil {
    return fmt.Errorf("target unreachable, skipping grooms: %w", err)
}
const maxReasonableGrooms = 100
if grooms > maxReasonableGrooms { grooms = maxReasonableGrooms }

Try / catch

conns, err := smb2Grooms(address, grooms, header)
if err != nil {
    var derr *net.OpError
    if errors.As(err, &derr) && isRefused(derr.Err) {
        // back off and retry with fewer grooms
    }
    return err
}

Prevention

When it happens

Trigger: exploit → smb2Grooms(address, grooms, header) → net.Dial fails on iteration i: connection refused, host unreachable, or the target's backlog is exhausted by the groom flood itself (too many grooms).

Common situations: Setting grooms too high exhausts the target's connection backlog or triggers rate limiting; firewall blocks subsequent connections; target drops connections under load.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/9598794cda1e131f. Report an issue: GitHub.