shadow1ng/fscan · error

failed to negotiate

Error message

failed to negotiate: %s

What it means

This error wraps a failure of smbClientNegotiate during smb1AnonymousConnectIPC (plugins/services/ms17010_exp.go:147). Negotiation sends a hardcoded SMB1 Negotiate Protocol request and reads the reply via smb1GetResponse; any transport failure or malformed reply is surfaced here. It means the TCP connection was established but the SMB protocol handshake did not complete.

Solutions

  1. Confirm the port actually speaks SMB (banner/protocol detection) rather than assuming 445 is SMB
  2. Check whether the target supports SMB1; if it is SMB2-only, this SMB1-based exploit path will not work
  3. Inspect the wrapped smb1GetResponse error to distinguish timeout (retry) vs reset (target refusing SMB1)
  4. Retry with backoff; transient resets during mass scanning are common
Defensive patterns

Strategy: retry

Validate before calling

// Confirm SMB1 negotiation works on a throwaway connection first
func canNegotiateSMB1(address string) bool {
    conn, err := net.DialTimeout("tcp", address, 10*time.Second)
    if err != nil { return false }
    defer conn.Close()
    _ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
    return smbClientNegotiate(conn) == nil
}

Type guard

func isNegotiateFailure(err error) bool {
    return strings.HasPrefix(err.Error(), "failed to negotiate:")
}

Try / catch

for attempt := 0; attempt < 3; attempt++ {
    header, conn, err := smb1AnonymousConnectIPC(addr)
    if err == nil { use(conn); return }
    if !isNegotiateFailure(err) { return err } // only retry negotiate failures
    time.Sleep(time.Duration(1<<attempt) * time.Second)
}

Prevention

When it happens

Trigger: smbClientNegotiate's buf.WriteTo(conn) fails (peer reset the connection right after accept), or smb1GetResponse fails: NetBIOS read error/timeout, non-zero message type byte, response shorter than the 32-byte SMB header, short read, or unparseable header.

Common situations: Target is not an SMB service (some other daemon on 445 answering garbage); honeypots or proxies that accept TCP then close; SMB servers that immediately negotiate SMB2-only and drop SMB1 clients; rate-limiting devices resetting sessions.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at plugins/services/ms17010_exp.go:147

	buf.Write(size)
	buf.Write(sc)
	return buf.Bytes()
}

func smb1AnonymousConnectIPC(address string) (*smbHeader, net.Conn, error) {
	conn, err := net.DialTimeout("tcp", address, 10*time.Second)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to connect host: %s", err)
	}
	var ok bool
	defer func() {
		if !ok {
			_ = conn.Close()
		}
	}()
	err = smbClientNegotiate(conn)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to negotiate: %s", err)
	}
	raw, header, err := smb1AnonymousLogin(conn)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to login with anonymous: %s", err)
	}
	_, err = getOSName(raw)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to get OS name: %s", err)
	}
	//fmt.Println("OS:", osName)
	header, err = treeConnectAndX(conn, address, header.UserID)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to tree connect AndX: %s", err)
	}
	ok = true
	return header, conn, nil
}

View on GitHub (pinned to 95cc12e753)