projectdiscovery/nuclei · error

DsGetNCChanges returned no objects

Error message

DsGetNCChanges returned no objects

What it means

The replication RPC completed without a transport-level fault but the response contained zero objects, so there is no secret to return. This happens when the resolved DN points at an object that carries no replicated secret (a container or organizational unit), or the DC silently filtered the object out.

Source

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

	// 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),
		LMHash:             hex.EncodeToString(o.LMHash),
		UserAccountControl: o.UserAccountControl,
		PwdLastSet:         o.PwdLastSet,
	}
	for _, h := range o.NTHashHistory {
		out.NTHashHistory = append(out.NTHashHistory, hex.EncodeToString(h))
	}
	for _, h := range o.LMHashHistory {
		out.LMHashHistory = append(out.LMHashHistory, hex.EncodeToString(h))
	}
	return out, nil

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Target a concrete security principal (user or computer sAMAccountName)
  2. Let the library resolve the target from sAMAccountName or SID instead of hand-writing the DN
  3. Re-verify the object still exists via LDAP immediately before the sync

Example fix

// before
const s = c.DCSync('OU=Employees,DC=acme,DC=local'); // OU carries no secrets

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

Strategy: validation

Validate before calling

// Only sync objects that are security principals
if strings.HasPrefix(dn, "OU=") || !isPrincipal(dn) {
    return fmt.Errorf("refusing to sync non-principal DN %q", dn)
}

Type guard

func looksLikePrincipalDN(dn string) bool {
    return strings.HasPrefix(dn, "CN=") && !strings.HasPrefix(dn, "OU=")
}

Try / catch

if err != nil && strings.Contains(err.Error(), "returned no objects") {
    // DN resolved but no secret replicated: re-resolve target via sAMAccountName and retry once
}

Prevention

When it happens

Trigger: Target DN resolved to OU=... or CN=LostAndFound instead of a user/computer object; account exists but its secret attributes are excluded (e.g. RODC filtered attribute set); race where the object was deleted between crack-names and replication.

Common situations: Passing a raw DN that looks right but is an OU; syncing machine accounts on DCs with filtered attribute sets; chasing accounts found in stale enumeration data.

Related errors


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