projectdiscovery/nuclei · error

could not resolve %q to a DN

Error message

could not resolve %q to a DN

What it means

DCSync's target must resolve to a distinguished name. If the argument does not already start with CN=, the code cracks it twice: first as an NT4 account name (DOMAIN\\user) and then as a SID. If both DsCrackNames attempts fail or return an empty name, the target does not exist (or cannot be cracked by this principal) and this error is returned.

Source

Thrown at pkg/js/libs/secretsdump/secretsdump.go:156

	dcInfo, err := gpdrs.DsDomainControllerInfo(rpc, bind.Handle, c.Domain)
	if err != nil {
		return nil, fmt.Errorf("ds dc info: %w", err)
	}

	domainDN, err := gpdrs.GetDomainDN(rpc, bind.Handle, c.Domain)
	if err != nil {
		return nil, fmt.Errorf("ds domain dn: %w", err)
	}

	// Resolve target -> DN if it doesn't already look like one.
	userDN := target
	if len(target) < 3 || (target[:3] != "CN=" && target[:3] != "cn=") {
		cracked, err := gpdrs.DsCrackNames(rpc, bind.Handle, 7 /* DS_NT4_ACCOUNT_NAME */, 1 /* DS_FQDN_1779_NAME */, []string{c.Domain + "\\" + target})
		if err != nil || len(cracked) == 0 || cracked[0].Name == "" {
			cracked, err = gpdrs.DsCrackNames(rpc, bind.Handle, 11 /* DS_UNIQUE_ID_NAME (SID) */, 1, []string{target})
			if err != nil || len(cracked) == 0 || cracked[0].Name == "" {
				return nil, fmt.Errorf("could not resolve %q to a DN", target)
			}
		}
		userDN = cracked[0].Name
	}

	res, err := gpdrs.DsGetNCChanges(rpc, bind.Handle, domainDN, userDN, dcInfo.NtdsDsaObjectGuid, rpc.GetSessionKey())
	if err != nil {
		return nil, fmt.Errorf("DsGetNCChanges: %w", err)
	}
	if len(res.Objects) == 0 {
		return nil, fmt.Errorf("DsGetNCChanges returned no objects")
	}
	o := res.Objects[0]
	out := &Secret{
		SAMAccountName:     o.SAMAccountName,
		DistinguishedName:  o.DN,
		RID:                o.RID,
		NTHash:             hex.EncodeToString(o.NTHash),

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Pass the exact sAMAccountName, e.g. DCSync('krbtgt')
  2. Or pass the full DN directly: DCSync('CN=Administrator,CN=Users,DC=acme,DC=local')
  3. Or pass the objectSid: DCSync('S-1-5-21-...-500')
  4. Verify the account exists with an LDAP search before syncing

Example fix

// before
const s = c.DCSync('administrator@acme.local'); // UPN form cannot be cracked

// after
const s = c.DCSync('Administrator');
Defensive patterns

Strategy: validation

Validate before calling

// Resolve or verify the target before DCSync
if !strings.HasPrefix(target, "CN=") && !strings.HasPrefix(target, "S-1-") {
    // will be cracked as DOMAIN\\sAMAccountName — confirm it exists via LDAP first
    if !samExists(dc, domain, target) {
        return fmt.Errorf("target %q does not exist", target)
    }
}

Type guard

func isResolvableTarget(t string) bool {
    return strings.HasPrefix(t, "CN=") || strings.HasPrefix(t, "cn=") ||
        strings.HasPrefix(t, "S-1-") || !strings.Contains(t, "@")
}

Try / catch

secret, err := c.DCSync(target)
if err != nil && strings.Contains(err.Error(), "could not resolve") {
    // account name wrong or UPN form used: retry with sAMAccountName or SID
    secret, err = c.DCSync(samFromSid(sid))
}

Prevention

When it happens

Trigger: DCSync('Admistrator') with a typo; DCSync('user@acme.local') — a UPN is neither the NT4 sAMAccountName form nor a SID; account lives in another trusted domain; caller lacks rights to crack names.

Common situations: UPN passed where sAMAccountName is expected; usernames harvested from email addresses; deleted or renamed accounts.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/9e9e98eea175c320. Report an issue: GitHub.