shadow1ng/fscan · error

mssql: unexpected login token 0x%02x

Error message

mssql: unexpected login token 0x%02x

What it means

The login response contained a TDS token the parser does not understand. This minimal raw-login implementation only handles error/info/envchange/loginack/done tokens, so any other token in the login response aborts parsing.

Source

Thrown at plugins/services/mssql_raw.go:327

			if err != nil {
				return false, err
			}
			pos = next
		case tdsTokenLoginAck:
			next, err := mssqlSkipLen16(payload, pos)
			if err != nil {
				return false, err
			}
			result.sawLoginAck = true
			pos = next
		case tdsTokenDone, tdsTokenDoneProc, tdsTokenDoneInProc:
			if pos+12 > len(payload) {
				return false, fmt.Errorf("mssql: truncated done token")
			}
			status := binary.LittleEndian.Uint16(payload[pos : pos+2])
			return status&(tdsDoneError|tdsDoneSrvError) == 0, nil
		default:
			return false, fmt.Errorf("mssql: unexpected login token 0x%02x", token)
		}
	}
	return false, nil
}

func mssqlParseErrorToken(payload []byte, pos int) (mssqlRawError, int, error) {
	if pos+2 > len(payload) {
		return mssqlRawError{}, pos, fmt.Errorf("mssql: truncated error token")
	}
	size := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
	end := pos + 2 + size
	if size < 6 || end > len(payload) || pos+8 > len(payload) {
		return mssqlRawError{}, pos, fmt.Errorf("mssql: invalid error token size")
	}
	pos += 2
	number := int32(binary.LittleEndian.Uint32(payload[pos : pos+4]))
	pos += 4
	pos += 2

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Check whether the server demands SSPI/Kerberos — if so, SQL auth cannot complete without implementing token 0xED handling; use go-mssqldb with Integrated Security instead.
  2. Add a case for the reported token type (it is printed in the error) if it is benign and can be safely skipped.
  3. Verify TDS version negotiation — some tokens only appear at higher TDS versions.
  4. Capture the stream and confirm the token byte is where parsing believes it is (a prior token mis-skip can desynchronize the walk).

Example fix

// before
default:
    return false, fmt.Errorf("mssql: unexpected login token 0x%02x", token)
// after
case 0xed: // SSPI — not supported by this raw client
    return false, fmt.Errorf("mssql: server requested SSPI authentication; use SQL auth or a full driver")
default:
    return false, fmt.Errorf("mssql: unexpected login token 0x%02x", token)
Defensive patterns

Strategy: try-catch

Try / catch

_, err := mssqlRawLogin(ctx, host, port, user, pass, timeout)
if err != nil {
    var tok byte
    if n, _ := fmt.Sscanf(err.Error(), "mssql: unexpected login token 0x%02x", &tok); n == 1 && tok == 0xed {
        return fmt.Errorf("server requires SSPI auth; use a full driver")
    }
    return err
}

Prevention

When it happens

Trigger: mssqlParseLoginTokens' default branch fires on token bytes such as 0x81 (COLMETADATA), 0x81/0xA9/0xA8 result tokens, or SSPI (0xED) — typically when the server wants integrated auth or returns result sets during login.

Common situations: Windows/SSPI authentication requested by the server (token 0xED); servers emitting features this minimal client didn't negotiate; truly desynchronized/garbage streams.

Related errors


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