使用Python ldap3库无法为AD服务账号设置‘User cannot change password’ ACE权限求助
It looks like you're hitting a common pitfall with this AD setting: you're modifying the domain object's security descriptor instead of the user object's own security descriptor. The "User cannot change password" flag is controlled by an ACE on the user account itself, not the root domain. Let's walk through fixing your code and verifying the setup.
Core Issue: Wrong Object for ACL Modification
The Microsoft documentation specifies that this setting requires adding a deny ACE to the user object's DACL, not the domain's. Your current code targets the domain object ((&(objectCategory=domain))), which is why the checkbox isn't updating.
Corrected Code Steps
First, let's adjust your code to target the user's security descriptor instead:
1. Update the create_object_ace Function (Minor Tweaks)
Ensure the permission mask and flags are correctly set for the deny ACE:
def create_object_ace(privguid, sid): nace = ldaptypes.ACE() # Deny-type ACE nace['AceType'] = ldaptypes.ACCESS_DENIED_OBJECT_ACE.ACE_TYPE # No inheritance (apply only to this user object) nace['AceFlags'] = 0x00 acedata = ldaptypes.ACCESS_DENIED_OBJECT_ACE() # Set mask to ADS_RIGHT_DS_CONTROL_ACCESS (required for extended rights) acedata['Mask'] = ldaptypes.ACCESS_MASK() acedata['Mask']['Mask'] = 0x00000100 # Equivalent to ADS_RIGHT_DS_CONTROL_ACCESS # GUID for "Change Password" extended right acedata['ObjectType'] = string_to_bin(privguid) acedata['InheritedObjectType'] = b'' # Target the user's own SID (deny them the right to change their password) acedata['Sid'] = ldaptypes.LDAP_SID() acedata['Sid'].fromCanonical(sid) # Mark that we're specifying an object type (the extended right GUID) acedata['Flags'] = ldaptypes.ACCESS_DENIED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT nace['Ace'] = acedata return nace
2. Modify the User Object's Security Descriptor
Instead of querying the domain, fetch the user's own security descriptor and update it:
from ldap3 import Server, Connection, ALL, SUBTREE, MODIFY_REPLACE from impacket import ldaptypes from impacket.ldap.ldap import security_descriptor_control, string_to_bin # Initialize connection s = Server('ad_server.com', get_info=ALL) c = Connection(s, user="testdomain\\username", password="password", authentication=NTLM) c.bind() # 1. Fetch the target user's details (SID and security descriptor) c.search( search_base="DC=testad,DC=com", search_filter="(CN=svc_account_47)", search_scope=SUBTREE, attributes=['objectSid', 'nTSecurityDescriptor'] ) entry = c.entries[0] usersid = entry['objectSid'].value user_dn = entry.entry_dn # 2. Get the user's current security descriptor with DACL access controls = security_descriptor_control(sdflags=0x04) # Request DACL secDescData = entry['nTSecurityDescriptor'].raw_values[0] secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData) # 3. Ensure the DACL exists (create if missing) if secDesc['Dacl'] is None: secDesc['Dacl'] = ldaptypes.ACL() secDesc['Dacl']['Data'] = [] secDesc['Control'] |= 0x04 # Set SE_DACL_PRESENT flag # 4. Add the deny ACE to the user's DACL change_pwd_guid = 'ab721a53-1e2f-11d0-9819-00aa0040529b' secDesc['Dacl']['Data'].append(create_object_ace(change_pwd_guid, usersid)) # 5. Commit the modified security descriptor back to the user object updated_sd = secDesc.getData() c.modify( user_dn, {'nTSecurityDescriptor': (MODIFY_REPLACE, [updated_sd])}, controls=controls ) print(c.result) # Should return {'result': 0, 'description': 'success', ...} c.unbind()
Verification Steps
After running the code, confirm the setting is applied:
- Open Active Directory Users and Computers, navigate to the service account
- Go to Properties > Account tab
- Check if the "User cannot change password" checkbox is now ticked
- For deeper verification, use ADSI Edit:
- Navigate to the user object, open Properties > Security > Advanced
- Look for a deny ACE where the principal is the user itself, and the permission is "Change Password"
Additional Notes
- Permissions: Ensure your service account has sufficient rights to modify user security descriptors (Domain Admin, Account Operator, or delegated permissions for modifying user ACLs)
- ACE Priority: Deny ACEs take precedence over allow ACEs, so this will override any existing allow permissions for password changes on the user
- Impacket Version: Make sure you're using a recent version of Impacket to avoid bugs in LDAP security descriptor handling
内容的提问来源于stack exchange,提问作者user2273231

