Last active
April 29, 2020 15:20
-
-
Save 2XXE-SRA/f7593e97df26d515d1ca3359e6e103f9 to your computer and use it in GitHub Desktop.
modified from: https://github.com/tojo2k/pysddl/blob/master/sddl.py/sddl.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""A module for translating and manipulating SDDL strings. | |
SDDL strings are used by Microsoft to describe ACLs as described in | |
http://msdn.microsoft.com/en-us/library/aa379567.aspx. | |
Example: D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPLOCRRC;;;PU) | |
""" | |
__author__ = 'tojo2000@tojo2000.com (Tim Johnson)' | |
__version__ = '0.0.1' | |
__updated__ = '2020-04-29' | |
import re | |
# Using API functions from the pywin32 package is MUCH faster than WMI | |
# from win32security import LookupAccountSid,GetBinarySid | |
re_valid_string = re.compile('^[ADO][ADLU]?\:\(.*\)$') | |
re_perms = re.compile('\(([^\(\)]+)\)') | |
re_type = re.compile('^[DOGS]') | |
re_owner = re.compile('^O:[^:()]+(?=[DGS]:)') | |
re_group = re.compile('G:[^:()]+(?=[DOS]:)') | |
re_acl = re.compile('[DS]:.+$') | |
re_const = re.compile('(\w\w)') | |
re_non_acl = re.compile('[^:()]+$') | |
SDDL_TYPE = {'O': 'Owner', | |
'G': 'Group', | |
'D': 'DACL', | |
'S': 'SACL'} | |
ACCESS = {# ACE Types | |
'A' : 'ACCESS_ALLOWED', | |
'D' : 'ACCESS_DENIED', | |
'OA': 'ACCESS_ALLOWED_OBJECT', | |
'OD': 'ACCESS_DENIED_OBJECT', | |
'AU': 'SYSTEM_AUDIT', | |
'AL': 'SYSTEM_ALARM', | |
'OU': 'SYSTEM_AUDIT_OBJECT', | |
'OL': 'SYSTEM_ALARM_OBJECT', | |
# ACE Flags | |
'CI': 'CONTAINER_INHERIT', | |
'OI': 'OBJECT_INHERIT', | |
'NP': 'NO_PROPAGATE_INHERIT', | |
'IO': 'INHERIT_ONLY', | |
'ID': 'INHERITED', | |
'SA': 'SUCCESSFUL_ACCESS', | |
'FA': 'FAILED_ACCESS', | |
# Generic Access Rights | |
'GA': 'GENERIC_ALL', | |
'GR': 'GENERIC_READ', | |
'GW': 'GENERIC_WRITE', | |
'GX': 'GENERIC_EXECUTE', | |
# Standard Access Rights | |
'RC': 'READ_CONTROL', | |
'SD': 'DELETE', | |
'WD': 'WRITE_DAC', | |
'WO': 'WRITE_OWNER', | |
# Directory Service Object Access Rights | |
'RP': 'DS_READ_PROP', | |
'WP': 'DS_WRITE_PROP', | |
'CC': 'DS_CREATE_CHILD', | |
'DC': 'DS_DELETE_CHILD', | |
'LC': 'DS_LIST', | |
'SW': 'DS_SELF', | |
'LO': 'DS_LIST_OBJECT', | |
'DT': 'DS_DELETE_TREE', | |
'CR': 'DS_CONTROL_ACCESS', | |
# File Access Rights | |
'FA': 'FILE_ALL_ACCESS', | |
'FR': 'FILE_GENERIC_READ', | |
'FW': 'FILE_GENERIC_WRITE', | |
'FX': 'FILE_GENERIC_EXECUTE', | |
# Registry Access Rights | |
'KA': 'KEY_ALL_ACCESS', | |
'KR': 'KEY_READ', | |
'KW': 'KEY_WRITE', | |
'KE': 'KEY_EXECUTE'} | |
""" | |
Access Mask: 32-bits | |
___________________________________ | |
| Bit(s) | Meaning | | |
----------------------------------- | |
| 0 - 15 | Object Access Rights | | |
| 16 - 22 | Standard Access Rights | | |
| 23 | Can access security ACL | | |
| 24 - 27 | Reserved | | |
| 28 - 31 | Generic Access Rights | | |
----------------------------------- | |
""" | |
ACCESS_HEX = { | |
# Generic Access Rights | |
0x10000000: 'GA', | |
0x20000000: 'GX', | |
0x40000000: 'GW', | |
0x80000000: 'GR', | |
# Standard Access Rights | |
0x00010000: 'SD', | |
0x00020000: 'RC', | |
0x00040000: 'WD', | |
0x00080000: 'WO', | |
# Object Access Rights | |
0x00000001: 'CC', | |
0x00000002: 'DC', | |
0x00000004: 'LC', | |
0x00000008: 'SW', | |
0x00000010: 'RP', | |
0x00000020: 'WP', | |
0x00000040: 'DT', | |
0x00000080: 'LO', | |
0x00000100: 'CR'} | |
TRUSTEE = {'AO': 'Account Operators', | |
'RU': 'Pre-Win2k Compatibility Access', | |
'AN': 'Anonymous', | |
'AU': 'Authenticated Users', | |
'BA': 'Administrators', | |
'BG': 'Guests', | |
'BO': 'Backup Operators', | |
'BU': 'Users', | |
'CA': 'Certificate Publishers', | |
'CD': 'Certificate Services DCOM Access', | |
'CG': 'Creator Group', | |
'CO': 'Creator Owner', | |
'DA': 'Domain Admins', | |
'DC': 'Domain Computers', | |
'DD': 'Domain Controllers', | |
'DG': 'Domain Guests', | |
'DU': 'Domain Users', | |
'EA': 'Enterprise Admins', | |
'ED': 'Enterprise Domain Controllers', | |
'RO': 'Enterprise Read-Only Domain Controllers', | |
'WD': 'Everyone', | |
'PA': 'Group Policy Admins', | |
'IU': 'Interactive Users', | |
'LA': 'Local Administrator', | |
'LG': 'Local Guest', | |
'LS': 'Local Service', | |
'SY': 'Local System', | |
'NU': 'Network', | |
'LW': 'Low Integrity', | |
'ME': 'Medium Integrity', | |
'HI': 'High Integrity', | |
'SI': 'System Integrity', | |
'NO': 'Network Configuration Operators', | |
'NS': 'Network Service', | |
'PO': 'Printer Operators', | |
'PS': 'Self', | |
'PU': 'Power Users', | |
'RS': 'RAS Servers', | |
'RD': 'Remote Desktop Users', | |
'RE': 'Replicator', | |
'RC': 'Restricted Code', | |
'SA': 'Schema Administrators', | |
'SO': 'Server Operators', | |
'SU': 'Service' | |
} | |
class Error(Exception): | |
"""Generic Error class.""" | |
class InvalidSddlStringError(Error): | |
"""The input string provided was not a valid SDDL string.""" | |
class InvalidSddlTypeError(Error): | |
"""The type specified must be O, G, D, or S.""" | |
class InvalidAceStringError(Error): | |
"""The ACE string provided was invalid.""" | |
#def TranslateSid(sid_string): | |
# """Translate a SID string to an account name. | |
# | |
# Args: | |
# sid_string: a SID in string form | |
# | |
# Returns: | |
# A string with the account name if the name resolves. | |
# None if the name is not found. | |
# """ | |
# account = LookupAccountSid(None, GetBinarySid(sid_string)) | |
# | |
# if account: | |
# if len(account[1]): | |
# return account[1] + '\\' + account[0] | |
# else: | |
# return account[0] | |
# | |
def SortAceByTrustee(x, y): | |
"""Custom sorting function to sort SDDL.ACE objects by trustee. | |
Args: | |
x: first object being compared | |
y: second object being compared | |
Returns: | |
The results of a cmp() between the objects. | |
""" | |
return cmp(x.trustee, y.trustee) | |
def AccessFromHex(hex): | |
"""Convert a hex access rights specifier to it's string equivalent. | |
Args: | |
hex: The hexadecimal string to be converted | |
Returns: | |
A string containing the converted access rights | |
""" | |
hex = int(hex, 16) | |
rights = "" | |
for spec in ACCESS_HEX.items(): | |
if hex & spec[0]: | |
rights += spec[1] | |
return rights | |
class ACE(object): | |
"""Represents an access control entry.""" | |
def __init__(self, ace_string, access_constants): | |
"""Initializes the SDDL::ACE object. | |
Args: | |
ace_string: a string representing a single access control entry | |
access_constants: a dictionary of access constants for translation | |
Raises: | |
InvalidAceStringError: If the ace string provided doesn't appear to be | |
valid. | |
""" | |
self.ace_string = ace_string | |
LOCAL_ACCESS = access_constants | |
self.flags = [] | |
self.perms = [] | |
fields = ace_string.split(';') | |
if len(fields) != 6: | |
raise InvalidAceStringError | |
if (LOCAL_ACCESS[fields[0]]): | |
self.ace_type = LOCAL_ACCESS[fields[0]] | |
else: | |
self.ace_type = fields[0] | |
for flag in re.findall(re_const, fields[1]): | |
if LOCAL_ACCESS[flag]: | |
self.flags.append(LOCAL_ACCESS[flag]) | |
else: | |
self.flags.append(flag) | |
if fields[2][0:2] == '0x': # If specified in hex | |
fields[2] = AccessFromHex(fields[2]) | |
for perm in re.findall(re_const, fields[2]): | |
if LOCAL_ACCESS[perm]: | |
self.perms.append(LOCAL_ACCESS[perm]) | |
else: | |
self.perms.append(perm) | |
self.perms.sort() | |
self.object_type = fields[3] | |
self.inherited_type = fields[4] | |
self.trustee = None | |
if fields[5] in TRUSTEE: | |
self.trustee = TRUSTEE[fields[5]] | |
#if not self.trustee: | |
# self.trustee = TranslateSid(fields[5]) | |
if not self.trustee: | |
self.trustee = 'Unknown or invalid SID.' | |
class SDDL(object): | |
"""Represents an SDDL string.""" | |
def __init__(self, sddl_string, target=None): | |
"""Initializes the SDDL object. | |
Args: | |
input_string: The SDDL string representation of the ACL | |
target: Some values of the SDDL string change their meaning depending | |
on the type of object they describe. | |
Note: The only supported type right now is 'service' | |
Raises: | |
SDDLInvalidStringError: if the string doesn't appear to be valid | |
""" | |
self.target = target | |
self.sddl_string = sddl_string | |
self.sddl_type = None | |
self.acl = [] | |
sddl_owner_part = re.search(re_owner, sddl_string) | |
sddl_group_part = re.search(re_group, sddl_string) | |
sddl_acl_part = re.search(re_acl, sddl_string) | |
self.ACCESS = ACCESS | |
self.group_sid = None | |
self.group_account = None | |
self.owner_sid = None | |
self.owner_account = None | |
if self.target == 'service': | |
self.ACCESS['CC'] = 'Query Configuration' | |
self.ACCESS['DC'] = 'Change Configuration' | |
self.ACCESS['LC'] = 'Query State' | |
self.ACCESS['SW'] = 'Enumerate Dependencies' | |
self.ACCESS['RP'] = 'Start' | |
self.ACCESS['WP'] = 'Stop' | |
self.ACCESS['DT'] = 'Pause' | |
self.ACCESS['LO'] = 'Interrogate' | |
self.ACCESS['CR'] = 'User Defined' | |
self.ACCESS['SD'] = 'Delete' | |
self.ACCESS['RC'] = 'Read the Security Descriptor' | |
self.ACCESS['WD'] = 'Change Permissions' | |
self.ACCESS['WO'] = 'Change Owner' | |
for match in (sddl_owner_part, sddl_group_part, sddl_acl_part): | |
if not match: | |
continue | |
part = match.group() | |
sddl_prefix = re.match(re_type, part).group() | |
if sddl_prefix in ('D', 'S'): | |
if sddl_prefix in SDDL_TYPE: | |
self.sddl_type = SDDL_TYPE[sddl_prefix] | |
else: | |
raise InvalidSddlTypeError | |
for perms in re.findall(re_perms, part): | |
self.acl.append(ACE(perms, self.ACCESS)) | |
elif (sddl_prefix == 'G'): | |
self.group_sid = re.search(re_non_acl, part).group() | |
if self.group_sid in TRUSTEE: | |
self.group_account = TRUSTEE[self.group_sid] | |
#else: | |
# self.group_account = TranslateSid(self.group_sid) | |
if not self.group_account: | |
self.group_account = 'Unknown' | |
elif (sddl_prefix == 'O'): | |
self.owner_sid = re.search(re_non_acl, part).group() | |
if self.owner_sid in TRUSTEE: | |
self.owner_account = TRUSTEE[self.owner_sid] | |
#else: | |
# self.owner_account = TranslateSid(self.owner_sid) | |
if not self.owner_account: | |
self.owner_account = 'Unknown' | |
else: | |
raise InvalidSddlStringError |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment