2

I am trying to create a service account via the Ldap3 library in Python. The service account is successfully created, but there is a small problem. The "User cannot change password" checkboxes is not selected.

I did some research and found that to set this property I need to follow some steps given here : https://learn.microsoft.com/en-us/windows/win32/adsi/modifying-user-cannot-change - password-ldap-provider . The logic given in this link is in a different programming language than Python, so i tried to replicate the same logic in Python. From what we understand, I’ve implemented the feature, which executed successfully, but didn't see the expected effect on the AD server ("User cannot change password" checkbox is still unchecked).

Below is the code used to create and add the ACL object to the service account.

def create_object_ace(privguid, sid):
    print("creating ace object")
    nace = ldaptypes.ACE()
    nace['AceType'] =     ldaptypes.ACCESS_DENIED_OBJECT_ACE.ACE_TYPE
nace['AceFlags'] = 0x00
acedata = ldaptypes.ACCESS_DENIED_OBJECT_ACE()
acedata['Mask'] = ldaptypes.ACCESS_MASK()
acedata['Mask']['Mask'] = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_CONTROL_ACCESS
acedata['ObjectType'] = string_to_bin(privguid)
acedata['InheritedObjectType'] = b''
acedata['Sid'] = ldaptypes.LDAP_SID()
acedata['Sid'].fromCanonical(sid)
assert sid == acedata['Sid'].formatCanonical()
acedata['Flags'] = ldaptypes.ACCESS_DENIED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT
nace['Ace'] = acedata
return nace

s = Server('ad_server.com', get_info=ALL)
c = Connection(s, user="testdomain\\username", password="password", authentication=NTLM)
 c.search(search_base="DC=testad,DC=com", search_filter="(CN=svc_account_47)",
     search_scope=SUBTREE, attributes=['objectSid', 'sAMAccountName'])

entry = c.entries[0]
usersid = entry['objectSid'].value

controls = security_descriptor_control(sdflags=0x04)
 c.search(search_base="DC=testahs,DC=com", search_filter='(&(objectCategory=domain))',
     attributes=['SAMAccountName', 'nTSecurityDescriptor'], controls=controls)
entry = c.entries[0]
secDescData =.    entry['nTSecurityDescriptor'].raw_values[0]
secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData)

secDesc['Dacl']['Data'].append(create_object_ace('ab721a53-1e2f-11d0-9819-00aa0040529b', usersid)) # This GUID is for 'User cannot change password'

dn = entry.entry_dn
data = secDesc.getData()

c.modify(dn, {'nTSecurityDescriptor': (ldap3.MODIFY_REPLACE, [data])}, controls=controls)

print(c.result)  

# gives -> {'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'modifyResponse'}

The Python code reference is from the following link:

https://github.com/SecureAuthCorp/impacket/blob/master/impacket/examples/ntlmrelayx/attacks/ldapattack.py

Can someone help me here. Thanks in advance.

4
  • entry = c.entries[0] usersid = entry['objectSid'].value That doesnt parse the value Commented Jul 29, 2022 at 13:00
  • AFTER fixing the SID, i now get the following error entry = c.entries[0] usersid = entry['objectSid'].value That doesnt parse the value, after i managed to get the SID i now face the followingf probkem: creating ace object Traceback (most recent call last): File "C:\problem2.py", line 103, in <module> secDesc['Dacl']['Data'].append(create_object_ace('ab721a53-1e2f-11d0-9819-00aa0040529b', usersid)) # This GUID is for 'User cannot change password' File "C:\problem2.py", line 80, in create_object_ace acedata['Sid'].fromCanonical(sid) Commented Jul 29, 2022 at 13:18
  • items = canonical.split('-') AttributeError: 'Attribute' object has no attribute 'split' Commented Jul 29, 2022 at 13:26
  • Okay, so found the sid wasn’t being set as a string but a byte object, however even know it says success it still fails to apply the checkbox Commented Jul 29, 2022 at 23:30

1 Answer 1

0

The only way I could do this in Python was this:

import uuid
import bonsai
from bonsai import LDAPClient
from bonsai.active_directory import SecurityDescriptor

def set_not_change_pass(self, userdn):
    client = LDAPClient(f"ldap://{self._server}")
    client.set_credentials("SIMPLE", user=f"CN={self._srvaccname},CN=Users,{self._dc}",
                           password=self._srvaccpass)

    with client.connect() as conn:
        entry = conn.search(
            userdn, 0, attrlist=["ntSecurityDescriptor", "userAccountControl"]
        )[0]
        uac = bonsai.active_directory.UserAccountControl(entry["userAccountControl"][0])
        sec_desc = bonsai.active_directory.SecurityDescriptor.from_binary(
            entry["ntSecurityDescriptor"][0]
        )

        new_dacl_aces = []
        for ace in sec_desc.dacl.aces:
            if ace.object_type == uuid.UUID("ab721a53-1e2f-11d0-9819-00aa0040529b"):
                # Find change password ACE and change it to deny.
                new_ace = bonsai.active_directory.ACE(
                    bonsai.active_directory.ACEType.ACCESS_DENIED_OBJECT,
                    ace.flags,
                    ace.mask,
                    ace.trustee_sid,
                    ace.object_type,
                    ace.inherited_object_type,
                    ace.application_data,
                )

                # Insert new deny ACEs to the front of the list.
                new_dacl_aces.insert(0, new_ace)
            else:
                new_dacl_aces.append(ace)

        new_dacl = bonsai.active_directory.ACL(sec_desc.dacl.revision, new_dacl_aces)
        new_sec_desc = bonsai.active_directory.SecurityDescriptor(
            sec_desc.control,
            sec_desc.owner_sid,
            sec_desc.group_sid,
            sec_desc.sacl,
            new_dacl,
            sec_desc.revision,
            sec_desc.sbz1,
        )

        entry.change_attribute(
            "ntSecurityDescriptor", bonsai.LDAPModOp.REPLACE, new_sec_desc.to_binary()
        )
        uac.properties["accountdisable"] = False
        entry.change_attribute("userAccountControl", bonsai.LDAPModOp.REPLACE, uac.value)
        entry.modify()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.