You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Python ldap3库无法为AD服务账号设置‘User cannot change password’ ACE权限求助

Fixing "User cannot change password" Checkbox Not Being Set via Python ldap3/Impacket

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:

  1. Open Active Directory Users and Computers, navigate to the service account
  2. Go to Properties > Account tab
  3. Check if the "User cannot change password" checkbox is now ticked
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 20:47:36