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

  1. Obtain a valid Kerberos ticket before scanning: run kinit (or use the domain account's credentials) on the scanning host and verify with klist.
  2. Verify the supplied client credentials are valid and the account is not locked/disabled.
  3. Synchronize clocks between the scanning host and the DC (w32tm /resync) — Kerberos tolerates at most ~5 minutes skew.
  4. 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

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.