shadow1ng/fscan · error
GSSAPI bind: %w
Error message
GSSAPI bind: %w
What it means
After dialing the DC over LDAP, connectToDomain attempts a Kerberos GSSAPI bind using the plugin's client credentials with the SPN "ldap/<dcHost>". If the bind is rejected or fails, the connection is closed and the error is wrapped as "GSSAPI bind: %w". This is an authentication failure against the directory, not a network failure.
Source
Thrown at plugins/local/systeminfo_dc_windows.go:97
client, err := gssapi.NewSSPIClient()
if err != nil {
return nil, fmt.Errorf("SSPI: %w", err)
}
defer func() { _ = client.Close() }()
conn, err := ldap.DialURL(ldapURL(dcHost, 389))
if err != nil {
if ipv4, resolveErr := resolveIPv4(dcHost); resolveErr == nil {
conn, err = ldap.DialURL(ldapURL(ipv4, 389))
}
if err != nil {
return nil, fmt.Errorf("LDAP dial: %w", err)
}
}
if err := conn.GSSAPIBind(client, fmt.Sprintf("ldap/%s", dcHost), ""); err != nil {
_ = conn.Close()
return nil, fmt.Errorf("GSSAPI bind: %w", err)
}
baseDN, err := p.getBaseDN(conn, domain)
if err != nil {
_ = conn.Close()
return nil, err
}
return &domainInfo{Domain: domain, BaseDN: baseDN, LDAPConn: conn}, nil
}
func (p *SystemInfoPlugin) findDC(domain string) (string, error) {
if out, err := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain)).Output(); err == nil {
for _, line := range strings.Split(string(out), "\n") {
if common.ContainsAny(line, "svr hostname", "service") {
parts := strings.Split(line, "=")
if len(parts) > 1 {
host := strings.TrimSpace(parts[len(parts)-1])View on GitHub (pinned to 95cc12e753)
Solutions
- Obtain a valid Kerberos ticket before scanning: run kinit (or use the domain account's credentials) on the scanning host and verify with klist.
- Verify the supplied client credentials are valid and the account is not locked/disabled.
- Synchronize clocks between the scanning host and the DC (w32tm /resync) — Kerberos tolerates at most ~5 minutes skew.
- Confirm dcHost matches the DC's registered SPN (ldap/<fqdn>); use the FQDN rather than an IP or alias.
Example fix
// before: bind fails because no ticket exists
conn, err := ldap.DialURL(ldapURL(dcHost, 389))
err = conn.GSSAPIBind(client, fmt.Sprintf("ldap/%s", dcHost), "") // GSSAPI bind: ...
// after: ensure a ticket first (caller-side setup)
// kinit user@CORP.EXAMPLE.COM
// klist # verify krb5 ticket
conn, err := ldap.DialURL(ldapURL(dcHost, 389))
err = conn.GSSAPIBind(client, fmt.Sprintf("ldap/%s", dcHost), "") Defensive patterns
Strategy: try-catch
Validate before calling
// Verify a Kerberos ticket exists before the scan
out, err := exec.Command("klist").Output()
if err != nil || !strings.Contains(string(out), "krbtgt") {
return fmt.Errorf("no Kerberos ticket: run kinit first")
} Try / catch
result := plugin.Scan(ctx, host, session)
if result != nil && !result.Success && strings.HasPrefix(result.Error.Error(), "GSSAPI bind:") {
// treat as auth failure: refresh credentials/ticket, resync time, do not blind-retry
} Prevention
- Always kinit (or supply valid domain credentials) before running AD plugins.
- Keep scanner clock in sync with the DC (w32tm) to avoid Kerberos skew rejection.
- Use the DC's FQDN so the ldap/<host> SPN matches what the KDC expects.
When it happens
Trigger: conn.GSSAPIBind(client, fmt.Sprintf("ldap/%s", dcHost), "") returns non-nil: invalid/expired Kerberos ticket or credentials, missing cached TGT (kinit not run), SPN mismatch, clock skew breaking Kerberos, or the account being locked out/disabled.
Common situations: Scanner machine not domain-joined and no Kerberos ticket obtained (no kinit), wrong credentials supplied to the client, time drift >5 minutes between scanner and DC breaking Kerberos, DC rejecting the ldap/<host> SPN because hostname case/DNS alias differs from the registered SPN.
Related errors
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/d1e5edcc77d45238.
Report an issue: GitHub.