findDelegation.py and GetNPUsers.py silently truncate results at 999 (missing paged search)
findDelegation.py and GetNPUsers.py query LDAP with sizeLimit=999 and no SimplePagedResultsControl. When a domain has more than 999 matching objects, both scripts truncate results at 999 with no warning. They catch the sizeLimitExceeded error and log it only at DEBUG level.
PR #1498 fixed this in GetUserSPNs.py but left findDelegation.py and GetNPUsers.py unchanged. Both still contain the TODO comment "Until we implement paged queries."
Configuration
- Impacket version: current
master(commit0b1a949) - Python version: 3.12
- Target OS: Windows Server 2022 domain controller, 100K+ user domain
Debug Output With Command String
findDelegation.py:
# 1,099 user accounts with TrustedForDelegation set (+ 1 DC = 1,100 total)
impacket-findDelegation -debug 'corp.lab/Administrator:password' -dc-ip 10.10.10.11
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[...]
[+] sizeLimitExceeded exception caught, giving up and processing the data received
[+] Total of records returned 1003Output: 999 rows. Last entry: scale_998. 101 delegated accounts dropped with no warning.
(1003 includes both SearchResultEntry entries and SearchResultReference referrals. Only entries print; the 999-entry limit applies to entries, referrals are separate.)
GetNPUsers.py:
# 1,099 user accounts with DONT_REQUIRE_PREAUTH set
impacket-GetNPUsers -debug 'corp.lab/Administrator:password' -dc-ip 10.10.10.11 -request
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[...]
[+] sizeLimitExceeded exception caught, giving up and processing the data received
[+] Total of records returned 1003Output: 999 users returned by LDAP, 998 AS-REP hashes retrieved. Last entry: scale_999. 100 accounts not returned by the query.
Note: the -usersfile flag bypasses LDAP enumeration and is unaffected.
PCAP
Not applicable (LDAP paging behavior, not a wire-level issue).
Additional context
Why 999 and not 1000:
Active Directory's default MaxPageSize policy is 1000. The client-requested sizeLimit=999 falls below that, so AD honors 999 and returns sizeLimitExceeded when more results exist. Omitting sizeLimit (default 0 = unlimited) lets AD enforce its own 1000-entry limit server-side. Either way, the server drops results above the limit unless the client uses paged search controls.
Affected code:
examples/findDelegation.pyline 134:sizeLimit=999, nosearchControlsexamples/GetNPUsers.pyline 241:sizeLimit=999, nosearchControlsexamples/GetUserSPNs.pyline 280-285: fixed withSimplePagedResultsControl(PR #1498)
findDelegation.py also has a second sizeLimit=999 at line 193 (RBCD principal resolution sub-query). That lookup resolves one principal at a time, so it won't reach 999, but it uses the same unbounded pattern.
Impact: A pentester running either script against a large AD environment gets incomplete results with no indication anything was dropped. They conclude enumeration is complete while missing hundreds of delegated or AS-REP roastable accounts, both high-value attack paths that change the outcome of an assessment.
Suggested fix:
Apply SimplePagedResultsControl to both scripts, matching the pattern from PR #1498 (GetUserSPNs.py):
paged_search_control = ldapasn1.SimplePagedResultsControl(criticality=True, size=1000)
resp = ldapConnection.search(searchFilter=searchFilter,
attributes=[...],
searchControls=[paged_search_control])Remove sizeLimit=999; the paged control handles result size. LDAPConnection.search() handles the cookie continuation loop.
I have a PR ready and will link it here shortly.
Source: fortra/impacket