shadow1ng/fscan · error

mssql: login acknowledgement not received

Error message

mssql: login acknowledgement not received

What it means

This error means the TDS login handshake completed without the server sending a LoginAck token (0xAD). The library performs a raw prelogin + Login7 exchange and treats a missing LoginAck as a failed/indeterminate login — typically authentication or protocol negotiation did not conclude successfully even though no explicit error token was parsed.

Source

Thrown at plugins/services/mssql_raw.go:111

	if err := mssqlSendPrelogin(conn); err != nil {
		return result, err
	}
	if err := mssqlReadPrelogin(conn); err != nil {
		return result, err
	}
	result.sawPrelogin = true

	if err := mssqlSendLogin7(conn, host, username, password); err != nil {
		return result, err
	}
	if err := mssqlReadLoginResponse(conn, result); err != nil {
		return result, err
	}
	if len(result.errors) > 0 {
		return result, result.errors[len(result.errors)-1]
	}
	if !result.sawLoginAck {
		return result, fmt.Errorf("mssql: login acknowledgement not received")
	}
	return result, nil
}

func mssqlSendPrelogin(w io.Writer) error {
	fields := map[byte][]byte{
		tdsPreloginVersion:    {0, 0, 0, 0, 0, 0},
		tdsPreloginEncryption: {tdsEncryptNotSupported},
		tdsPreloginInstOpt:    {0},
		tdsPreloginThreadID:   {0, 0, 0, 0},
		tdsPreloginMARS:       {0},
	}

	keys := make([]int, 0, len(fields))
	for k := range fields {
		keys = append(keys, int(k))
	}
	sort.Ints(keys)

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the username/password is correct and the account is not locked — retry with known-good credentials via a full driver (go-mssqldb) to confirm.
  2. Enable TLS on the login (send encryption-not-supported vs require correctly) or point the check at a server supporting TDS 7.4.
  3. Capture the raw packet stream (tcpdump/Wireshark) and inspect whether a 0xAD token was actually sent but skipped by the parser.
  4. Check that the target host:port really is SQL Server and not a proxy/other TDS-speaking service.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before trusting a positive result, require both prelogin and loginack:
res, err := mssqlRawLogin(ctx, host, port, user, pass, timeout)
if err == nil && (!res.sawPrelogin || !res.sawLoginAck) {
    // treat as failed login
}

Type guard

func loginFullyAcknowledged(r *mssqlRawResult) bool {
    return r != nil && r.sawPrelogin && r.sawLoginAck && len(r.errors) == 0
}

Try / catch

res, err := mssqlRawLogin(ctx, host, port, user, pass, 5*time.Second)
if err != nil {
    if strings.Contains(err.Error(), "login acknowledgement not received") {
        // fall back to a full driver or surface 'authentication/protocol failed'
    }
    return err
}

Prevention

When it happens

Trigger: Calling mssqlRawLogin where mssqlReadLoginResponse returns without result.sawLoginAck set: the server returned a response stream containing only env-change/done tokens, the stream ended after an early done token, or parsing stopped after an info token.

Common situations: Server requires encryption or a newer TDS version and silently rejects the plain Login7; connection to a non-SQL-Server service that replies with a malformed token stream; network middleboxes truncating the response.

Related errors


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