From df60216e73c9e2f91a75f4dde8eab07bfab84654 Mon Sep 17 00:00:00 2001 From: Ebrix Date: Mon, 3 Nov 2025 08:30:14 +0100 Subject: [PATCH] Add a folder to regroup windows helper functions/class fix import import update regclient fail fucking encoding Add toSDDL for discussions docstring format consistency rename win_security to security flake8 de mort scapy scapy padabom remove accessrights related code remove Access rights size comments --- scapy/layers/msrpce/mspac.py | 2 +- scapy/layers/smb2.py | 845 ------------ scapy/layers/smbclient.py | 2 +- scapy/layers/smbserver.py | 2 +- scapy/layers/windows/__init__.py | 21 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 617 bytes .../__pycache__/registry.cpython-313.pyc | Bin 0 -> 37666 bytes .../__pycache__/win_security.cpython-313.pyc | Bin 0 -> 47985 bytes scapy/layers/windows/registry.py | 1134 +++++++++++++++++ scapy/layers/windows/security.py | 936 ++++++++++++++ scapy/modules/ldaphero.py | 2 +- scapy/modules/ticketer.py | 2 +- 12 files changed, 2096 insertions(+), 850 deletions(-) create mode 100644 scapy/layers/windows/__init__.py create mode 100644 scapy/layers/windows/__pycache__/__init__.cpython-313.pyc create mode 100644 scapy/layers/windows/__pycache__/registry.cpython-313.pyc create mode 100644 scapy/layers/windows/__pycache__/win_security.cpython-313.pyc create mode 100644 scapy/layers/windows/registry.py create mode 100644 scapy/layers/windows/security.py diff --git a/scapy/layers/msrpce/mspac.py b/scapy/layers/msrpce/mspac.py index 6185673c804..1a96a9afd13 100644 --- a/scapy/layers/msrpce/mspac.py +++ b/scapy/layers/msrpce/mspac.py @@ -68,7 +68,7 @@ _NTLMPayloadField, _NTLMPayloadPacket, ) -from scapy.layers.smb2 import WINNT_SID +from scapy.layers.windows.security import WINNT_SID # sect 2.4 diff --git a/scapy/layers/smb2.py b/scapy/layers/smb2.py index fcfb3702b26..f1993b2a84a 100644 --- a/scapy/layers/smb2.py +++ b/scapy/layers/smb2.py @@ -15,7 +15,6 @@ import functools import hashlib import os -import re import struct from scapy.automaton import select_objects @@ -28,7 +27,6 @@ ConditionalField, FieldLenField, FieldListField, - FlagValue, FlagsField, IP6Field, IPField, @@ -767,849 +765,6 @@ class FileStreamInformation(Packet): ] -# [MS-DTYP] sect 2.4.1 - - -class WINNT_SID_IDENTIFIER_AUTHORITY(Packet): - fields_desc = [ - StrFixedLenField("Value", b"\x00\x00\x00\x00\x00\x01", length=6), - ] - - def default_payload_class(self, payload): - return conf.padding_layer - - -# [MS-DTYP] sect 2.4.2 - - -class WINNT_SID(Packet): - fields_desc = [ - ByteField("Revision", 1), - FieldLenField("SubAuthorityCount", None, count_of="SubAuthority", fmt="B"), - PacketField( - "IdentifierAuthority", - WINNT_SID_IDENTIFIER_AUTHORITY(), - WINNT_SID_IDENTIFIER_AUTHORITY, - ), - FieldListField( - "SubAuthority", - [0], - LEIntField("", 0), - count_from=lambda pkt: pkt.SubAuthorityCount, - ), - ] - - def default_payload_class(self, payload): - return conf.padding_layer - - _SID_REG = re.compile(r"^S-(\d)-(\d+)((?:-\d+)*)$") - - @staticmethod - def fromstr(x): - m = WINNT_SID._SID_REG.match(x) - if not m: - raise ValueError("Invalid SID format !") - rev, authority, subauthority = m.groups() - return WINNT_SID( - Revision=int(rev), - IdentifierAuthority=WINNT_SID_IDENTIFIER_AUTHORITY( - Value=struct.pack(">Q", int(authority))[2:] - ), - SubAuthority=[int(x) for x in subauthority[1:].split("-")], - ) - - def summary(self): - return "S-%s-%s%s" % ( - self.Revision, - struct.unpack(">Q", b"\x00\x00" + self.IdentifierAuthority.Value)[0], - ( - ("-%s" % "-".join(str(x) for x in self.SubAuthority)) - if self.SubAuthority - else "" - ), - ) - - -# https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers - -WELL_KNOWN_SIDS = { - # Universal well-known SID - "S-1-0-0": "Null SID", - "S-1-1-0": "Everyone", - "S-1-2-0": "Local", - "S-1-2-1": "Console Logon", - "S-1-3-0": "Creator Owner ID", - "S-1-3-1": "Creator Group ID", - "S-1-3-2": "Owner Server", - "S-1-3-3": "Group Server", - "S-1-3-4": "Owner Rights", - "S-1-4": "Non-unique Authority", - "S-1-5": "NT Authority", - "S-1-5-80-0": "All Services", - # NT well-known SIDs - "S-1-5-1": "Dialup", - "S-1-5-113": "Local account", - "S-1-5-114": "Local account and member of Administrators group", - "S-1-5-2": "Network", - "S-1-5-3": "Batch", - "S-1-5-4": "Interactive", - "S-1-5-6": "Service", - "S-1-5-7": "Anonymous Logon", - "S-1-5-8": "Proxy", - "S-1-5-9": "Enterprise Domain Controllers", - "S-1-5-10": "Self", - "S-1-5-11": "Authenticated Users", - "S-1-5-12": "Restricted Code", - "S-1-5-13": "Terminal Server User", - "S-1-5-14": "Remote Interactive Logon", - "S-1-5-15": "This Organization", - "S-1-5-17": "IUSR", - "S-1-5-18": "System (or LocalSystem)", - "S-1-5-19": "NT Authority (LocalService)", - "S-1-5-20": "Network Service", - "S-1-5-32-544": "Administrators", - "S-1-5-32-545": "Users", - "S-1-5-32-546": "Guests", - "S-1-5-32-547": "Power Users", - "S-1-5-32-548": "Account Operators", - "S-1-5-32-549": "Server Operators", - "S-1-5-32-550": "Print Operators", - "S-1-5-32-551": "Backup Operators", - "S-1-5-32-552": "Replicators", - "S-1-5-32-554": r"Builtin\Pre-Windows 2000 Compatible Access", - "S-1-5-32-555": r"Builtin\Remote Desktop Users", - "S-1-5-32-556": r"Builtin\Network Configuration Operators", - "S-1-5-32-557": r"Builtin\Incoming Forest Trust Builders", - "S-1-5-32-558": r"Builtin\Performance Monitor Users", - "S-1-5-32-559": r"Builtin\Performance Log Users", - "S-1-5-32-560": r"Builtin\Windows Authorization Access Group", - "S-1-5-32-561": r"Builtin\Terminal Server License Servers", - "S-1-5-32-562": r"Builtin\Distributed COM Users", - "S-1-5-32-568": r"Builtin\IIS_IUSRS", - "S-1-5-32-569": r"Builtin\Cryptographic Operators", - "S-1-5-32-573": r"Builtin\Event Log Readers", - "S-1-5-32-574": r"Builtin\Certificate Service DCOM Access", - "S-1-5-32-575": r"Builtin\RDS Remote Access Servers", - "S-1-5-32-576": r"Builtin\RDS Endpoint Servers", - "S-1-5-32-577": r"Builtin\RDS Management Servers", - "S-1-5-32-578": r"Builtin\Hyper-V Administrators", - "S-1-5-32-579": r"Builtin\Access Control Assistance Operators", - "S-1-5-32-580": r"Builtin\Remote Management Users", - "S-1-5-32-581": r"Builtin\Default Account", - "S-1-5-32-582": r"Builtin\Storage Replica Admins", - "S-1-5-32-583": r"Builtin\Device Owners", - "S-1-5-64-10": "NTLM Authentication", - "S-1-5-64-14": "SChannel Authentication", - "S-1-5-64-21": "Digest Authentication", - "S-1-5-80": "NT Service", - "S-1-5-80-0": "All Services", - "S-1-5-83-0": r"NT VIRTUAL MACHINE\Virtual Machines", -} - - -# [MS-DTYP] sect 2.4.3 - -_WINNT_ACCESS_MASK = { - 0x80000000: "GENERIC_READ", - 0x40000000: "GENERIC_WRITE", - 0x20000000: "GENERIC_EXECUTE", - 0x10000000: "GENERIC_ALL", - 0x02000000: "MAXIMUM_ALLOWED", - 0x01000000: "ACCESS_SYSTEM_SECURITY", - 0x00100000: "SYNCHRONIZE", - 0x00080000: "WRITE_OWNER", - 0x00040000: "WRITE_DACL", - 0x00020000: "READ_CONTROL", - 0x00010000: "DELETE", -} - - -# [MS-DTYP] sect 2.4.4.1 - - -WINNT_ACE_FLAGS = { - 0x01: "OBJECT_INHERIT", - 0x02: "CONTAINER_INHERIT", - 0x04: "NO_PROPAGATE_INHERIT", - 0x08: "INHERIT_ONLY", - 0x10: "INHERITED_ACE", - 0x40: "SUCCESSFUL_ACCESS", - 0x80: "FAILED_ACCESS", -} - - -class WINNT_ACE_HEADER(Packet): - fields_desc = [ - ByteEnumField( - "AceType", - 0, - { - 0x00: "ACCESS_ALLOWED", - 0x01: "ACCESS_DENIED", - 0x02: "SYSTEM_AUDIT", - 0x03: "SYSTEM_ALARM", - 0x04: "ACCESS_ALLOWED_COMPOUND", - 0x05: "ACCESS_ALLOWED_OBJECT", - 0x06: "ACCESS_DENIED_OBJECT", - 0x07: "SYSTEM_AUDIT_OBJECT", - 0x08: "SYSTEM_ALARM_OBJECT", - 0x09: "ACCESS_ALLOWED_CALLBACK", - 0x0A: "ACCESS_DENIED_CALLBACK", - 0x0B: "ACCESS_ALLOWED_CALLBACK_OBJECT", - 0x0C: "ACCESS_DENIED_CALLBACK_OBJECT", - 0x0D: "SYSTEM_AUDIT_CALLBACK", - 0x0E: "SYSTEM_ALARM_CALLBACK", - 0x0F: "SYSTEM_AUDIT_CALLBACK_OBJECT", - 0x10: "SYSTEM_ALARM_CALLBACK_OBJECT", - 0x11: "SYSTEM_MANDATORY_LABEL", - 0x12: "SYSTEM_RESOURCE_ATTRIBUTE", - 0x13: "SYSTEM_SCOPED_POLICY_ID", - }, - ), - FlagsField( - "AceFlags", - 0, - 8, - WINNT_ACE_FLAGS, - ), - LenField("AceSize", None, fmt=" conditional expression - cond_expr = None - if hasattr(self.payload, "ApplicationData"): - # Parse tokens - res = [] - for ct in self.payload.ApplicationData.Tokens: - if ct.TokenType in [ - # binary operators - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x88, 0x8e, 0x8f, - 0xa0, 0xa1 - ]: - t1 = res.pop(-1) - t0 = res.pop(-1) - tt = ct.sprintf("%TokenType%") - if ct.TokenType in [0xa0, 0xa1]: # && and || - res.append(f"({t0}) {tt} ({t1})") - else: - res.append(f"{t0} {tt} {t1}") - elif ct.TokenType in [ - # unary operators - 0x87, 0x8d, 0xa2, 0x89, 0x8a, 0x8b, 0x8c, 0x91, 0x92, 0x93 - ]: - t0 = res.pop(-1) - tt = ct.sprintf("%TokenType%") - res.append(f"{tt}{t0}") - elif ct.TokenType in [ - # values - 0x01, 0x02, 0x03, 0x04, 0x10, 0x18, 0x50, 0x51, 0xf8, 0xf9, - 0xfa, 0xfb - ]: - def lit(ct): - if ct.TokenType in [0x10, 0x18]: # literal strings - return '"%s"' % ct.value - elif ct.TokenType == 0x50: # composite - return "({%s})" % ",".join(lit(x) for x in ct.value) - else: - return str(ct.value) - res.append(lit(ct)) - elif ct.TokenType == 0x00: # padding - pass - else: - raise ValueError("Unhandled token type %s" % ct.TokenType) - if len(res) != 1: - raise ValueError("Incomplete SDDL !") - cond_expr = "(%s)" % res[0] - return { - "ace-flags-string": ace_flag_string, - "sid-string": sid_string, - "mask": mask, - "object-guid": object_guid, - "inherited-object-guid": inherit_object_guid, - "cond-expr": cond_expr, - } - # fmt: on - - def toSDDL(self, accessMask=None): - """ - Return SDDL - """ - data = self.extractData(accessMask=accessMask) - ace_rights = "" # TODO - if self.AceType in [0x9, 0xA, 0xB, 0xD]: # Conditional ACE - conditional_ace_type = { - 0x09: "XA", - 0x0A: "XD", - 0x0B: "XU", - 0x0D: "ZA", - }[self.AceType] - return "D:(%s)" % ( - ";".join( - x - for x in [ - conditional_ace_type, - data["ace-flags-string"], - ace_rights, - str(data["object-guid"]), - str(data["inherited-object-guid"]), - data["sid-string"], - data["cond-expr"], - ] - if x is not None - ) - ) - else: - ace_type = { - 0x00: "A", - 0x01: "D", - 0x02: "AU", - 0x05: "OA", - 0x06: "OD", - 0x07: "OU", - 0x11: "ML", - 0x13: "SP", - }[self.AceType] - return "(%s)" % ( - ";".join( - x - for x in [ - ace_type, - data["ace-flags-string"], - ace_rights, - str(data["object-guid"]), - str(data["inherited-object-guid"]), - data["sid-string"], - data["cond-expr"], - ] - if x is not None - ) - ) - - -# [MS-DTYP] sect 2.4.4.2 - - -class WINNT_ACCESS_ALLOWED_ACE(Packet): - fields_desc = [ - FlagsField("Mask", 0, -32, _WINNT_ACCESS_MASK), - PacketField("Sid", WINNT_SID(), WINNT_SID), - ] - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_ACE, AceType=0x00) - - -# [MS-DTYP] sect 2.4.4.3 - - -class WINNT_ACCESS_ALLOWED_OBJECT_ACE(Packet): - fields_desc = [ - FlagsField("Mask", 0, -32, _WINNT_ACCESS_MASK), - FlagsField( - "Flags", - 0, - -32, - { - 0x00000001: "OBJECT_TYPE_PRESENT", - 0x00000002: "INHERITED_OBJECT_TYPE_PRESENT", - }, - ), - ConditionalField( - UUIDField("ObjectType", None, uuid_fmt=UUIDField.FORMAT_LE), - lambda pkt: pkt.Flags.OBJECT_TYPE_PRESENT, - ), - ConditionalField( - UUIDField("InheritedObjectType", None, uuid_fmt=UUIDField.FORMAT_LE), - lambda pkt: pkt.Flags.INHERITED_OBJECT_TYPE_PRESENT, - ), - PacketField("Sid", WINNT_SID(), WINNT_SID), - ] - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_OBJECT_ACE, AceType=0x05) - - -# [MS-DTYP] sect 2.4.4.4 - - -class WINNT_ACCESS_DENIED_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_ACE, AceType=0x01) - - -# [MS-DTYP] sect 2.4.4.5 - - -class WINNT_ACCESS_DENIED_OBJECT_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_OBJECT_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_OBJECT_ACE, AceType=0x06) - - -# [MS-DTYP] sect 2.4.4.17.4+ - - -class WINNT_APPLICATION_DATA_LITERAL_TOKEN(Packet): - def default_payload_class(self, payload): - return conf.padding_layer - - -# fmt: off -WINNT_APPLICATION_DATA_LITERAL_TOKEN.fields_desc = [ - ByteEnumField( - "TokenType", - 0, - { - # [MS-DTYP] sect 2.4.4.17.5 - 0x00: "Padding token", - 0x01: "Signed int8", - 0x02: "Signed int16", - 0x03: "Signed int32", - 0x04: "Signed int64", - 0x10: "Unicode", - 0x18: "Octet String", - 0x50: "Composite", - 0x51: "SID", - # [MS-DTYP] sect 2.4.4.17.6 - 0x80: "==", - 0x81: "!=", - 0x82: "<", - 0x83: "<=", - 0x84: ">", - 0x85: ">=", - 0x86: "Contains", - 0x88: "Any_of", - 0x8e: "Not_Contains", - 0x8f: "Not_Any_of", - 0x89: "Member_of", - 0x8a: "Device_Member_of", - 0x8b: "Member_of_Any", - 0x8c: "Device_Member_of_Any", - 0x90: "Not_Member_of", - 0x91: "Not_Device_Member_of", - 0x92: "Not_Member_of_Any", - 0x93: "Not_Device_Member_of_Any", - # [MS-DTYP] sect 2.4.4.17.7 - 0x87: "Exists", - 0x8d: "Not_Exists", - 0xa0: "&&", - 0xa1: "||", - 0xa2: "!", - # [MS-DTYP] sect 2.4.4.17.8 - 0xf8: "Local attribute", - 0xf9: "User Attribute", - 0xfa: "Resource Attribute", - 0xfb: "Device Attribute", - } - ), - ConditionalField( - # Strings - LEIntField("length", 0), - lambda pkt: pkt.TokenType in [ - 0x10, # Unicode string - 0x18, # Octet string - 0xf8, 0xf9, 0xfa, 0xfb, # Attribute tokens - 0x50, # Composite - ] - ), - ConditionalField( - MultipleTypeField( - [ - ( - LELongField("value", 0), - lambda pkt: pkt.TokenType in [ - 0x01, # signed int8 - 0x02, # signed int16 - 0x03, # signed int32 - 0x04, # signed int64 - ] - ), - ( - StrLenFieldUtf16("value", b"", length_from=lambda pkt: pkt.length), - lambda pkt: pkt.TokenType in [ - 0x10, # Unicode string - 0xf8, 0xf9, 0xfa, 0xfb, # Attribute tokens - ] - ), - ( - StrLenField("value", b"", length_from=lambda pkt: pkt.length), - lambda pkt: pkt.TokenType == 0x18, # Octet string - ), - ( - PacketListField("value", [], WINNT_APPLICATION_DATA_LITERAL_TOKEN, - length_from=lambda pkt: pkt.length), - lambda pkt: pkt.TokenType == 0x50, # Composite - ), - - ], - StrFixedLenField("value", b"", length=0), - ), - lambda pkt: pkt.TokenType in [ - 0x01, 0x02, 0x03, 0x04, 0x10, 0x18, 0xf8, 0xf9, 0xfa, 0xfb, 0x50 - ] - ), - ConditionalField( - # Literal - ByteEnumField("sign", 0, { - 0x01: "+", - 0x02: "-", - 0x03: "None", - }), - lambda pkt: pkt.TokenType in [ - 0x01, # signed int8 - 0x02, # signed int16 - 0x03, # signed int32 - 0x04, # signed int64 - ] - ), - ConditionalField( - # Literal - ByteEnumField("base", 0, { - 0x01: "Octal", - 0x02: "Decimal", - 0x03: "Hexadecimal", - }), - lambda pkt: pkt.TokenType in [ - 0x01, # signed int8 - 0x02, # signed int16 - 0x03, # signed int32 - 0x04, # signed int64 - ] - ), -] -# fmt: on - - -class WINNT_APPLICATION_DATA(Packet): - fields_desc = [ - StrFixedLenField("Magic", b"\x61\x72\x74\x78", length=4), - PacketListField( - "Tokens", - [], - WINNT_APPLICATION_DATA_LITERAL_TOKEN, - ), - ] - - def default_payload_class(self, payload): - return conf.padding_layer - - -# [MS-DTYP] sect 2.4.4.6 - - -class WINNT_ACCESS_ALLOWED_CALLBACK_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc + [ - PacketField( - "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA - ), - ] - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_CALLBACK_ACE, AceType=0x09) - - -# [MS-DTYP] sect 2.4.4.7 - - -class WINNT_ACCESS_DENIED_CALLBACK_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_CALLBACK_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_CALLBACK_ACE, AceType=0x0A) - - -# [MS-DTYP] sect 2.4.4.8 - - -class WINNT_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_OBJECT_ACE.fields_desc + [ - PacketField( - "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA - ), - ] - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE, AceType=0x0B) - - -# [MS-DTYP] sect 2.4.4.9 - - -class WINNT_ACCESS_DENIED_CALLBACK_OBJECT_ACE(Packet): - fields_desc = WINNT_ACCESS_DENIED_OBJECT_ACE.fields_desc + [ - PacketField( - "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA - ), - ] - - -bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_CALLBACK_OBJECT_ACE, AceType=0x0C) - - -# [MS-DTYP] sect 2.4.4.10 - - -class WINNT_SYSTEM_AUDIT_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_ACE, AceType=0x02) - - -# [MS-DTYP] sect 2.4.4.11 - - -class WINNT_SYSTEM_AUDIT_OBJECT_ACE(Packet): - # doc is wrong. - fields_desc = WINNT_ACCESS_ALLOWED_OBJECT_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_OBJECT_ACE, AceType=0x07) - - -# [MS-DTYP] sect 2.4.4.12 - - -class WINNT_SYSTEM_AUDIT_CALLBACK_ACE(Packet): - fields_desc = WINNT_SYSTEM_AUDIT_ACE.fields_desc + [ - PacketField( - "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA - ), - ] - - -bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_CALLBACK_ACE, AceType=0x0D) - - -# [MS-DTYP] sect 2.4.4.13 - - -class WINNT_SYSTEM_MANDATORY_LABEL_ACE(Packet): - fields_desc = WINNT_SYSTEM_AUDIT_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_MANDATORY_LABEL_ACE, AceType=0x11) - - -# [MS-DTYP] sect 2.4.4.14 - - -class WINNT_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE(Packet): - fields_desc = WINNT_SYSTEM_AUDIT_OBJECT_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE, AceType=0x0F) - -# [MS-DTYP] sect 2.4.10.1 - - -class CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1(_NTLMPayloadPacket): - _NTLM_PAYLOAD_FIELD_NAME = "Data" - fields_desc = [ - LEIntField("NameOffset", 0), - LEShortEnumField( - "ValueType", - 0, - { - 0x0001: "CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64", - 0x0002: "CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64", - 0x0003: "CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING", - 0x0005: "CLAIM_SECURITY_ATTRIBUTE_TYPE_SID", - 0x0006: "CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN", - 0x0010: "CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING", - }, - ), - LEShortField("Reserved", 0), - FlagsField( - "Flags", - 0, - -32, - { - 0x0001: "CLAIM_SECURITY_ATTRIBUTE_NON_INHERITABLE", - 0x0002: "CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE", - 0x0004: "CLAIM_SECURITY_ATTRIBUTE_USE_FOR_DENY_ONLY", - 0x0008: "CLAIM_SECURITY_ATTRIBUTE_DISABLED_BY_DEFAULT", - 0x0010: "CLAIM_SECURITY_ATTRIBUTE_DISABLED", - 0x0020: "CLAIM_SECURITY_ATTRIBUTE_MANDATORY", - }, - ), - LEIntField("ValueCount", 0), - FieldListField( - "ValueOffsets", [], LEIntField("", 0), count_from=lambda pkt: pkt.ValueCount - ), - _NTLMPayloadField( - "Data", - lambda pkt: 16 + pkt.ValueCount * 4, - [ - ConditionalField( - StrFieldUtf16("Name", b""), - lambda pkt: pkt.NameOffset, - ), - # TODO: Values - ], - offset_name="Offset", - ), - ] - - -# [MS-DTYP] sect 2.4.4.15 - - -class WINNT_SYSTEM_RESOURCE_ATTRIBUTE_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc + [ - PacketField( - "AttributeData", - CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1(), - CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, - ) - ] - - -bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_RESOURCE_ATTRIBUTE_ACE, AceType=0x12) - -# [MS-DTYP] sect 2.4.4.16 - - -class WINNT_SYSTEM_SCOPED_POLICY_ID_ACE(Packet): - fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc - - -bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_SCOPED_POLICY_ID_ACE, AceType=0x13) - -# [MS-DTYP] sect 2.4.5 - - -class WINNT_ACL(Packet): - fields_desc = [ - ByteField("AclRevision", 2), - ByteField("Sbz1", 0x00), - # Total size including header: - # AclRevision(1) + Sbz1(1) + AclSize(2) + AceCount(2) + Sbz2(2) - FieldLenField( - "AclSize", - None, - length_of="Aces", - adjust=lambda _, x: x + 8, - fmt=" bytes - return ( - _NTLM_post_build( - self, - pkt, - self.OFFSET, - { - "OwnerSid": 4, - "GroupSid": 8, - "SACL": 12, - "DACL": 16, - }, - config=[ - ("Offset", _NTLM_ENUM.OFFSET), - ], - ) - + pay - ) - - # [MS-FSCC] 2.4.2 FileAllInformation diff --git a/scapy/layers/smbclient.py b/scapy/layers/smbclient.py index d143cd29a01..6ad8bfe61c2 100644 --- a/scapy/layers/smbclient.py +++ b/scapy/layers/smbclient.py @@ -58,11 +58,11 @@ SMB_Dialect, SMB_Header, ) +from scapy.layers.windows.security import SECURITY_DESCRIPTOR from scapy.layers.smb2 import ( DirectTCP, FileAllInformation, FileIdBothDirectoryInformation, - SECURITY_DESCRIPTOR, SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2, SMB2_CREATE_REQUEST_LEASE, SMB2_CREATE_REQUEST_LEASE_V2, diff --git a/scapy/layers/smbserver.py b/scapy/layers/smbserver.py index be1c68ee247..99c6ba7d89f 100644 --- a/scapy/layers/smbserver.py +++ b/scapy/layers/smbserver.py @@ -55,6 +55,7 @@ SMBTree_Connect_AndX, SMB_Header, ) +from scapy.layers.windows.security import SECURITY_DESCRIPTOR from scapy.layers.smb2 import ( DFS_REFERRAL_ENTRY1, DFS_REFERRAL_V3, @@ -76,7 +77,6 @@ FileStandardInformation, FileStreamInformation, NETWORK_INTERFACE_INFO, - SECURITY_DESCRIPTOR, SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2, SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE, SMB2_CREATE_QUERY_ON_DISK_ID, diff --git a/scapy/layers/windows/__init__.py b/scapy/layers/windows/__init__.py new file mode 100644 index 00000000000..c707ea2f415 --- /dev/null +++ b/scapy/layers/windows/__init__.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: YOLO +# This file is part of Scapy +# See https://scapy.net/ for more information + + +""" +This package implements Windows-specific high level helpers. +It makes it easier to use Scapy Windows related objects. + +It currently contains helpers for the Windows Registry. + +Note that if you want to tweak specific fields of the underlying +protocols, you will have to use the lower level objects directly. +""" + +# Make sure config is loaded +from scapy.config import conf + +# Import the Windows RPC extension +# pylint: disable-next=too-many-function-args +conf.exts.load("scapy-rpc") diff --git a/scapy/layers/windows/__pycache__/__init__.cpython-313.pyc b/scapy/layers/windows/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dac571387c4c4456f07d7267d68009557e40e1b6 GIT binary patch literal 617 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 literal 0 HcmV?d00001 diff --git a/scapy/layers/windows/__pycache__/win_security.cpython-313.pyc b/scapy/layers/windows/__pycache__/win_security.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4176d181c9e09fcd3a361a8a3fe5a374833839fc GIT binary patch literal 47985 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

BNC#xkb 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 b3?caLhxCJb3L*IJhm1iTxFAQ6iR1q-7~H#a literal 0 HcmV?d00001 diff --git a/scapy/layers/windows/registry.py b/scapy/layers/windows/registry.py new file mode 100644 index 00000000000..acbd4c312c1 --- /dev/null +++ b/scapy/layers/windows/registry.py @@ -0,0 +1,1134 @@ +# SPDX-License-Identifier: YOLO +# This file is part of Scapy +# See https://scapy.net/ for more information + +""" +This file implements Windows Registry related high level functions. +It provides easy to use functions to manipulate the registry. +If you want to tweak low level fields see directly +scapy.layers.msrpce.raw.ms_rrp. Otherwise, this module should +hopefully provide everything you need. +""" + +from enum import StrEnum, IntEnum, IntFlag +from typing import Generator, Optional + +from scapy.packet import Packet +from scapy.error import log_runtime + +from scapy.layers.windows.security import ( + SECURITY_DESCRIPTOR, +) +from scapy.layers.msrpce.rpcclient import DCERPC_Client +from scapy.layers.dcerpc import ( + NDRConformantArray, + NDRPointer, + NDRVaryingArray, +) + +# pylint: disable-next=import-error, no-name-in-module +from scapy.layers.msrpce.raw.ms_rrp import ( + OpenClassesRoot_Request, + OpenLocalMachine_Request, + OpenCurrentUser_Request, + OpenUsers_Request, + OpenCurrentConfig_Request, + OpenPerformanceData_Request, + OpenPerformanceText_Request, + OpenPerformanceNlsText_Request, + BaseRegOpenKey_Request, + BaseRegEnumKey_Request, + BaseRegEnumValue_Request, + BaseRegCloseKey_Request, + BaseRegQueryValue_Request, + BaseRegGetVersion_Request, + BaseRegQueryInfoKey_Request, + BaseRegQueryInfoKey_Response, + BaseRegGetKeySecurity_Request, + BaseRegSaveKey_Request, + BaseRegSetValue_Request, + BaseRegCreateKey_Request, + BaseRegDeleteKey_Request, + BaseRegDeleteValue_Request, + PRPC_SECURITY_DESCRIPTOR, + PRPC_SECURITY_ATTRIBUTES, + RPC_UNICODE_STRING, + NDRContextHandle, +) + + +class RootKeys(StrEnum): + """ + Standard root keys for the Windows registry + """ + + # Registry root keys + # These constants are used to specify the root keys of the Windows registry. + # The root keys are the top-level keys in the registry hierarchy. + + # Registry entries subordinate to this key define types + # (or classes) of documents and the + # properties associated with those types. + # The subkeys of the HKEY_CLASSES_ROOT key are a merged + # view of the following two subkeys: + HKEY_CLASSES_ROOT = "HKCR" + + # Registry entries subordinate to this key define the + # preferences of the current user. + # These preferences include the settings of environment + # variables, data on program groups, + # colors, printers, network connections, and application preferences. + # The HKEY_CURRENT_USER root key is a subkey of the + # HKEY_USERS root key, as described in + # section 3.1.1.8. + HKEY_CURRENT_USER = "HKCU" + + # Registry entries subordinate to this key define the + # physical state of the computer, + # including data on the bus type, system memory, and + # installed hardware and software. + HKEY_LOCAL_MACHINE = "HKLM" + + # This key contains information on the current + # hardware profile of the local computer. + # HKEY_CURRENT_CONFIG is an alias for + # HKEY_LOCAL_MACHINE\System\CurrentControlSet\Hardware Profiles\Current + HKEY_CURRENT_CONFIG = "HKCC" + + # This key define the default user configuration for + # new users on the local computer and the + # user configuration for the current user. + HKEY_USERS = "HKU" + + # Registry entries subordinate to this key allow + # access to performance data. + HKEY_PERFORMANCE_DATA = "HKPD" + + # Registry entries subordinate to this key reference + # the text strings that describe counters + # in U.S. English. + HKEY_PERFORMANCE_TEXT = "HKPT" + + # Registry entries subordinate to this key + # reference the text strings that describe + # counters in the local language of the area in + # which the computer is running. + HKEY_PERFORMANCE_NLSTEXT = "HKPN" + + def __new__(cls, value): + # 1. Strip and uppercase the raw input + normalized = value.strip().upper() + # 2. Create the enum member with the normalized value + obj = str.__new__(cls, normalized) + obj._value_ = normalized + return obj + + @classmethod + def from_value(cls, value: str): + """Convert a string to a RootKeys enum member.""" + value = value.strip().upper() + match value: + case "HKEY_CLASSES_ROOT": + value = RootKeys.HKEY_CLASSES_ROOT.value + case "HKEY_CURRENT_USER": + value = RootKeys.HKEY_CURRENT_USER.value + case "HKEY_LOCAL_MACHINE": + value = RootKeys.HKEY_LOCAL_MACHINE.value + case "HKEY_CURRENT_CONFIG": + value = RootKeys.HKEY_CURRENT_CONFIG.value + case "HKEY_USERS": + value = RootKeys.HKEY_USERS.value + case "HKEY_PERFORMANCE_DATA": + value = RootKeys.HKEY_PERFORMANCE_DATA.value + case "HKEY_PERFORMANCE_TEXT": + value = RootKeys.HKEY_PERFORMANCE_TEXT.value + case "HKEY_PERFORMANCE_NLSTEXT": + value = RootKeys.HKEY_PERFORMANCE_NLSTEXT.value + + try: + return cls(value) + except ValueError: + print(f"Unknown root key: {value}.") + + +class ErrorCodes(IntEnum): + """ + Error codes for registry operations + """ + + ERROR_SUCCESS = 0x00000000 + ERROR_FILE_NOT_FOUND = 0x00000002 + ERROR_PATH_NOT_FOUND = 0x00000003 + ERROR_ACCESS_DENIED = 0x00000005 + ERROR_INVALID_HANDLE = 0x00000006 + ERROR_NOT_SAME_DEVICE = 0x00000011 + ERROR_WRITE_PROTECT = 0x00000013 + ERROR_INVALID_PARAMETER = 0x00000057 + ERROR_CALL_NOT_IMPLEMENTED = 0x00000057 + ERROR_INVALID_NAME = 0x0000007B + ERROR_BAD_PATHNAME = 0x000000A1 + ERROR_ALREADY_EXISTS = 0x000000B7 + ERROR_NO_MORE_ITEMS = 0x00000103 + ERROR_NOACCESS = 0x000003E6 + ERROR_SUBKEY_NOT_FOUND = 0x000006F7 + ERROR_INSUFFICIENT_BUFFER = 0x0000007A + ERROR_MORE_DATA = 0x000000EA + + def __str__(self) -> str: + """ + Return the string representation of the error code. + :return: The string representation of the error code. + """ + + return self.name + + +class RegOptions(IntFlag): + """ + Registry options for registry keys + """ + + REG_OPTION_NON_VOLATILE = 0x00000000 + REG_OPTION_VOLATILE = 0x00000001 + REG_OPTION_CREATE_LINK = 0x00000002 + REG_OPTION_BACKUP_RESTORE = 0x00000004 + REG_OPTION_OPEN_LINK = 0x00000008 + REG_OPTION_DONT_VIRTUALIZE = 0x00000010 + + +class RegType(IntEnum): + """ + Registry value types + """ + + # These constants are used to specify the type of a registry value. + + REG_SZ = 1 # Unicode string + REG_EXPAND_SZ = 2 # Unicode string with environment variable expansion + REG_BINARY = 3 # Binary data + REG_DWORD = 4 # 32-bit unsigned integer + REG_DWORD_BIG_ENDIAN = 5 # 32-bit unsigned integer in big-endian format + REG_LINK = 6 # Symbolic link + REG_MULTI_SZ = 7 # Multiple Unicode strings + REG_QWORD = 11 # 64-bit unsigned integer + UNK = 99999 # fallback default + + @classmethod + def _missing_(cls, value): + log_runtime.info(f"Unknown registry type: {value}, using UNK") + unk = cls.UNK + unk.real_value = value + return unk + + def __new__(cls, value, real_value=None): + obj = int.__new__(cls, value) + obj._value_ = value + if real_value is None: + real_value = value + obj.real_value = real_value + return obj + + @classmethod + def fromvalue(cls, value: str | int) -> "RegType": + """Convert a string to a RegType enum member. + :param value: The string representation of the registry type. + :return: The corresponding RegType enum member. + """ + if isinstance(value, int): + try: + return cls(value) + except ValueError: + log_runtime.info(f"Unknown registry type: {value}, using UNK") + return cls.UNK + else: + value = value.strip().upper() + if "_" not in value: + value = "REG_" + value.lstrip("REG") + try: + return cls[value] + except (ValueError, KeyError): + log_runtime.info(f"Unknown registry type: {value}, using UNK") + return cls.UNK + + +class RegEntry: + """ + RegEntry to properly parse the data based on the type. + + :param reg_value: the name of the registry value (str) + :param reg_type: the type of the registry value (int) + :param reg_data: the data of the registry value (str) + """ + + def __init__(self, reg_value: str, reg_type: int, reg_data: bytes, from_str=False): + self.reg_value = reg_value + try: + self.reg_type = RegType(reg_type) + except ValueError: + self.reg_type = RegType.UNK + + if from_str: + self.reg_data = RegEntry.encode_data(self.reg_type, reg_data) + else: + self.reg_data = reg_data + + @staticmethod + def encode_data(reg_type: RegType, data: str | list[str]) -> bytes: + """ + Encode data based on the type. + """ + + match reg_type: + case RegType.REG_MULTI_SZ | RegType.REG_SZ | RegType.REG_EXPAND_SZ: + if reg_type == RegType.REG_MULTI_SZ: + # encode to multiple null terminated strings + if isinstance(data, str): + # if it was previously encoded, we remove the + # final \x00 and the final empty string + data = data.strip(b"\x00\x00\x00\x00") + encoded_data = ( + b"\x00\x00".join( + [x.strip().encode("utf-16le") for x in data.split()] + ) + + b"\x00\x00" # final \x00 + + b"\x00\x00" # final empty string + ) + elif isinstance(data, list): + # if it was previously encoded, we remove the + # final \x00 and the final empty string + if data[-1] == "": + data = data[:-1] + encoded_data = ( + b"\x00\x00".join( + [ + x.strip().strip("\x00\x00").encode("utf-16le") + for x in data + ] + ) + + b"\x00\x00" # final \x00 + + b"\x00\x00" # final empty string + ) + else: + log_runtime.error( + "Expected str or list[str] instance for data, got %s", + type(data), + ) + raise TypeError + + return encoded_data + + else: + return data.encode("utf-16le") + + case RegType.REG_BINARY: + if isinstance(data, bytes): + return data + return data.encode("utf-8").decode("unicode_escape").encode("latin1") + + case RegType.REG_DWORD | RegType.REG_QWORD: + # Use fixed sizes: 4 bytes for DWORD, 8 bytes for QWORD. + bit_length = 4 if reg_type == RegType.REG_DWORD else 8 + return int(data).to_bytes(bit_length, byteorder="little") + + case RegType.REG_DWORD_BIG_ENDIAN: + bit_length = 4 + return int(data).to_bytes(bit_length, byteorder="big") + + case RegType.REG_LINK: + return data.encode("utf-16le") + + case _: + return data.encode("utf-8").decode("unicode_escape").encode("latin1") + + @staticmethod + def decode_data(reg_type: RegType, data: bytes) -> str: + """ + Decode data based on the type. + """ + match reg_type: + case RegType.REG_MULTI_SZ | RegType.REG_SZ | RegType.REG_EXPAND_SZ: + if reg_type == RegType.REG_MULTI_SZ: + # decode multiple null terminated strings + return data.decode("utf-16le")[:-1].split("\x00") + else: + return data.decode("utf-16le") + + case RegType.REG_BINARY: + return data + + case RegType.REG_DWORD | RegType.REG_QWORD: + return int.from_bytes(data, byteorder="little") + + case RegType.REG_DWORD_BIG_ENDIAN: + return int.from_bytes(data, byteorder="big") + + case RegType.REG_LINK: + return data.decode("utf-16le") + + case _: + return data + + def __str__(self) -> str: + if self.reg_type == RegType.UNK: + return f"{self.reg_value} ({self.reg_type.name}: {self.reg_type.real_value}) {self.reg_data}" # noqa: E501 + return f"{self.reg_value} ({self.reg_type.name}: {self.reg_type.value}) {self.reg_data}" # noqa: E501 + + def __repr__(self) -> str: + return f"RegEntry({self.reg_value}, {self.reg_type}, {self.reg_data})" + + def __eq__(self, value): + return isinstance(value, RegEntry) and all( + [ + self.reg_data == value.reg_data, + self.reg_type == value.reg_type, + self.reg_data == value.reg_data, + ] + ) + + +class RegApi: + """ + High level Windows Registry API functions. + These functions use the low level RPC requests defined in ms_rrp to provide + easy to use functions to manipulate the Windows registry. + """ + + @staticmethod + def is_status_ok(status: int) -> Optional[bool]: + """ + Check the error code and raise an exception if it is not successful. + :param status: The error code to check. + """ + + try: + err = ErrorCodes(status) + if err not in [ + ErrorCodes.ERROR_SUCCESS, + ErrorCodes.ERROR_NO_MORE_ITEMS, + ErrorCodes.ERROR_MORE_DATA, + ]: + log_runtime.error( + "Error: %s - %s", hex(err.value), ErrorCodes(status).name + ) + return False + return True + except ValueError as exc: + log_runtime.error("Error: %s - Unknown error code", hex(status)) + raise ValueError(f"Error: {hex(status)} - Unknown error code") from exc + + @staticmethod + def get_root_key_handle( + client: DCERPC_Client, + root_key_name: RootKeys, + sam_desired: int = 0x2000000, # Maximum Allowed + ndr64: bool = True, + timeout: int = 5, + ) -> Optional[NDRContextHandle]: + """ + Get a handle to a root key. + + :param root_key_name: The name of the root key to open. + Must be one of the RootKeys enum values. + :param sam_desired: The desired access rights for the key. + :param ndr64: Whether to use NDR64 encoding. + :param ServerName: The server name. The ServerName SHOULD be + sent as NULL, and MUST be ignored + when it is received because binding to the server + is already complete at this stage + :return: The handle to the opened root key. + """ + + if root_key_name == RootKeys.HKEY_CLASSES_ROOT: + return client.sr1_req( + OpenClassesRoot_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + elif root_key_name == RootKeys.HKEY_CURRENT_USER: + return client.sr1_req( + OpenCurrentUser_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + elif root_key_name == RootKeys.HKEY_LOCAL_MACHINE: + return client.sr1_req( + OpenLocalMachine_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + elif root_key_name == RootKeys.HKEY_USERS: + return client.sr1_req( + OpenUsers_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + elif root_key_name == RootKeys.HKEY_CURRENT_CONFIG: + return client.sr1_req( + OpenCurrentConfig_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + elif root_key_name == RootKeys.HKEY_PERFORMANCE_DATA: + return client.sr1_req( + OpenPerformanceData_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + elif root_key_name == RootKeys.HKEY_PERFORMANCE_TEXT: + return client.sr1_req( + OpenPerformanceText_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + elif root_key_name == RootKeys.HKEY_PERFORMANCE_NLSTEXT: + return client.sr1_req( + OpenPerformanceNlsText_Request( + ServerName=None, + samDesired=sam_desired, + ndr64=ndr64, + ), + timeout=timeout, + ).phKey + else: + raise ValueError(f"Unknown root key: {root_key_name}") + + @staticmethod + def get_subkey_handle( + client: DCERPC_Client, + root_key_handle: NDRContextHandle, + subkey_path: str, + desired_access_rights: int = 0x2000000, # Maximum Allowed + options: RegOptions = RegOptions.REG_OPTION_NON_VOLATILE, + ndr64: bool = True, + timeout: int = 5, + ) -> NDRContextHandle: + """ + Get a handle to a subkey. + + :param client: The DCERPC client. + :param root_key_handle: The handle to the root key. + :param subkey_path: The name of the subkey to open. + :param desired_access_rights: The desired access rights for the subkey. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: The handle to the opened subkey. + """ + + # Ensure it is null-terminated and handle the special case of "." + if str(subkey_path) == ".": + subkey_path = "\x00" + elif not str(subkey_path).endswith("\x00"): + subkey_path = str(subkey_path) + "\x00" + + response = client.sr1_req( + BaseRegOpenKey_Request( + hKey=root_key_handle, + lpSubKey=RPC_UNICODE_STRING(Buffer=subkey_path), + samDesired=desired_access_rights, + dwOptions=options, + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while opening subkey %s", + hex(response.status), + subkey_path, + ) + raise ValueError(response.status) + + return response.phkResult + + @staticmethod + def get_version( + client: DCERPC_Client, + key_handle: NDRContextHandle, + ndr64: bool = True, + timeout: int = 5, + ) -> Packet: + """ + Get the version of the registry server. + + :param client: The DCERPC client. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: The response packet containing the version information. + """ + + response = client.sr1_req( + BaseRegGetVersion_Request( + hKey=key_handle, + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while getting version", hex(response.status) + ) + + return response + + @staticmethod + def get_key_info( + client: DCERPC_Client, + key_handle: NDRContextHandle, + ndr64: bool = True, + timeout: int = 5, + ) -> Optional[BaseRegQueryInfoKey_Response]: + """ + Get information about a given registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: The response packet containing the key information. + """ + + response = client.sr1_req( + BaseRegQueryInfoKey_Request( + hKey=key_handle, + lpClassIn=RPC_UNICODE_STRING(), + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while querying key info", hex(response.status) + ) + raise ValueError(response.status) + + return response + + @staticmethod + def get_key_security( + client: DCERPC_Client, + key_handle: NDRContextHandle, + security_information: int = None, + ndr64: bool = True, + timeout: int = 5, + ) -> SECURITY_DESCRIPTOR: + """ + Get the security descriptor of a given registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param security_information: The security information to retrieve. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: The response packet containing the security descriptor. + """ + + if security_information is None: + security_information = ( + 0x00000001 # OWNER_SECURITY_INFORMATION + | 0x00000002 # GROUP_SECURITY_INFORMATION + | 0x00000004 # DACL_SECURITY_INFORMATION + ) + + # Build initial request + req = BaseRegGetKeySecurity_Request( + hKey=key_handle, + SecurityInformation=security_information, + pRpcSecurityDescriptorIn=PRPC_SECURITY_DESCRIPTOR( + cbInSecurityDescriptor=512, # Initial size of the buffer + ), + ndr64=ndr64, + ) + + # Send request + response = client.sr1_req(req, timeout=timeout) + if response.status == ErrorCodes.ERROR_INSUFFICIENT_BUFFER: + # The buffer was too small, we need to retry with a larger one + req.pRpcSecurityDescriptorIn.cbInSecurityDescriptor = ( + response.pRpcSecurityDescriptorOut.cbInSecurityDescriptor + ) + response = client.sr1_req(req, timeout=timeout) + + # Check the response status + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while getting security", hex(response.status) + ) + return None + + sd = SECURITY_DESCRIPTOR( + response.pRpcSecurityDescriptorOut.valueof("lpSecurityDescriptor") + ) + + return sd + + @staticmethod + def enum_subkeys( + client: DCERPC_Client, + key_handle: NDRContextHandle, + ndr64: bool = True, + timeout: int = 5, + ) -> Generator[Packet, None, None]: + """ + Enumerate subkeys of a given registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: A generator yielding the responses for each enumerated subkey. + """ + index = 0 + + while True: + response = client.sr1_req( + BaseRegEnumKey_Request( + hKey=key_handle, + dwIndex=index, + lpNameIn=RPC_UNICODE_STRING(MaximumLength=1024), + lpClassIn=RPC_UNICODE_STRING(), + lpftLastWriteTime=None, + ndr64=ndr64, + ), + timeout=timeout, + ) + + # Send request + if response.status == ErrorCodes.ERROR_NO_MORE_ITEMS: + break + + # Check the response status + elif not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while enumerating keys", hex(response.status) + ) + raise ValueError(response.status) + + index += 1 + yield response + + @staticmethod + def enum_values( + client: DCERPC_Client, + key_handle: NDRContextHandle, + ndr64: bool = True, + timeout: int = 5, + ) -> Generator[Packet, None, None]: + """ + Enumerate values of a given registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: A generator yielding the responses for each enumerated value. + """ + index = 0 + + while True: + # Get the name and value at index `index` + response = client.sr1_req( + BaseRegEnumValue_Request( + hKey=key_handle, + dwIndex=index, + lpValueNameIn=RPC_UNICODE_STRING( + MaximumLength=2048, + Buffer=NDRPointer( + value=NDRConformantArray( + max_count=1024, value=NDRVaryingArray(value=b"") + ) + ), + ), + lpType=0, # pointer to type, set to 0 for query + lpData=None, # pointer to buffer + lpcbData=0, # pointer to buffer size + lpcbLen=0, # pointer to length + ndr64=ndr64, + ), + timeout=timeout, + ) + + if response.status == ErrorCodes.ERROR_NO_MORE_ITEMS: + break + + elif not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while enumerating values", hex(response.status) + ) + raise ValueError(response.status) + + # Get the value name and type + # for the name we got earlier + req = BaseRegQueryValue_Request( + hKey=key_handle, + lpValueName=response.valueof("lpValueNameOut"), + lpType=0, + lpcbData=1024, + lpcbLen=0, + lpData=NDRPointer( + value=NDRConformantArray( + max_count=1024, value=NDRVaryingArray(actual_count=0, value=b"") + ) + ), + ndr64=ndr64, + ) + + # Send request + response2 = client.sr1_req(req, timeout=timeout) + if response2.status == ErrorCodes.ERROR_MORE_DATA: + # The buffer was too small, we need to retry with a larger one + req.lpcbData = response2.lpcbData + req.lpData.value.max_count = response2.lpcbData.value + response2 = client.sr1_req(req, timeout=timeout) + + # Check the response status + if not RegApi.is_status_ok(response2.status): + log_runtime.error( + "got status %s while querying value", hex(response2.status) + ) + raise ValueError(response2.status) + + index += 1 + yield response, response2 + + @staticmethod + def get_value( + client: DCERPC_Client, + key_handle: NDRContextHandle, + value_name: str, + ndr64: bool = True, + timeout: int = 5, + ) -> Packet: + """ + Get the value of a given registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param value_name: The name of the value to retrieve. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: The response packet containing the value data. + """ + + response = client.sr1_req( + BaseRegQueryValue_Request( + hKey=key_handle, + lpValueName=value_name, + lpType=0, + lpcbData=1024, + lpcbLen=0, + lpData=NDRPointer( + value=NDRConformantArray( + max_count=1024, value=NDRVaryingArray(actual_count=0, value=b"") + ) + ), + ndr64=ndr64, + ), + timeout=timeout, + ) + + if response.status == ErrorCodes.ERROR_MORE_DATA: + # The buffer was too small, we need to retry with a larger one + response = client.sr1_req( + BaseRegQueryValue_Request( + hKey=key_handle, + lpValueName=value_name, + lpType=0, + lpcbData=response.lpcbData, + lpcbLen=0, + lpData=NDRPointer( + value=NDRConformantArray( + max_count=response.lpcbData.value, + value=NDRVaryingArray(actual_count=0, value=b""), + ) + ), + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while querying value %s", + hex(response.status), + value_name, + ) + + return response + + @staticmethod + def save_subkey( + client: DCERPC_Client, + key_handle: NDRContextHandle, + file_path: str, + security_attributes: PRPC_SECURITY_ATTRIBUTES = None, + ndr64: bool = True, + timeout: int = 5, + ) -> bool: + """ + Save a given registry key to a file. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param file_path: The path to the file where the key will be saved. + Default path is %WINDIR%\\System32, which is readable by all users. + :param security_attributes: Security attributes for the saved key. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: True if the key was saved successfully, False otherwise. + """ + + response = client.sr1_req( + BaseRegSaveKey_Request( + hKey=key_handle, + lpFile=RPC_UNICODE_STRING(Buffer=file_path), + pSecurityAttributes=security_attributes, + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error("Got status %s while saving key", hex(response.status)) + return False + + return True + + @staticmethod + def set_value( + client: DCERPC_Client, + key_handle: NDRContextHandle, + value_name: str, + value_type: RegType, + value_data: str | bytes, + ndr64: bool = True, + timeout: int = 5, + ) -> bool: + """ + Set a given value for a registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param value_name: The name of the value to set. + :param value_type: The type of the value to set. + :param value_data: The data of the value to set. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: True if the value was set successfully, False otherwise. + """ + + if not str(value_name).endswith("\x00"): + value_name = str(value_name) + "\x00" + + if isinstance(value_data, bytes): + data = value_data + else: + data = RegEntry.encode_data(value_type, value_data) + + response = client.sr1_req( + BaseRegSetValue_Request( + hKey=key_handle, + lpValueName=RPC_UNICODE_STRING(Buffer=value_name), + dwType=value_type.value, + cbData=len(data), + lpData=data, + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while setting value %s", + hex(response.status), + value_name, + ) + raise ValueError(response.status) + + return True + + @staticmethod + def create_subkey( + client: DCERPC_Client, + root_key_handle: NDRContextHandle, + subkey_path: str, + desired_access_rights: int = 0x2000000, # Maximum allowed + options: RegOptions = RegOptions.REG_OPTION_NON_VOLATILE, + security_attributes: PRPC_SECURITY_ATTRIBUTES = None, + ndr64: bool = True, + timeout: int = 5, + ) -> Packet: + """ + Create a given subkey under a registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the root key. + :param subkey_path: The name of the subkey to create. + :param desired_access_rights: The desired access rights for the subkey. + :param options: The options for the subkey. + :param security_attributes: Security attributes for the created key. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: The handle to the created subkey. + """ + + if not str(subkey_path).endswith("\x00"): + subkey_path = str(subkey_path) + "\x00" + + response = client.sr1_req( + BaseRegCreateKey_Request( + hKey=root_key_handle, + lpSubKey=RPC_UNICODE_STRING(Buffer=subkey_path), + samDesired=desired_access_rights, + dwOptions=options, + lpSecurityAttributes=security_attributes, + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while creating subkey %s", + hex(response.status), + subkey_path, + ) + return None + + return response + + @staticmethod + def delete_subkey( + client: DCERPC_Client, + root_key_handle: NDRContextHandle, + subkey_path: str, + ndr64: bool = True, + timeout: int = 5, + ) -> bool: + """ + Delete a given subkey from a registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the root key. + :param subkey_path: The name of the subkey to remove. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: True if the subkey was deleted successfully, False otherwise. + """ + + if not str(subkey_path).endswith("\x00"): + subkey_path = str(subkey_path) + "\x00" + + response = client.sr1_req( + BaseRegDeleteKey_Request( + hKey=root_key_handle, + lpSubKey=RPC_UNICODE_STRING(Buffer=subkey_path), + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while deleting subkey %s", + hex(response.status), + subkey_path, + ) + raise ValueError(response.status) + + return True + + @staticmethod + def delete_value( + client: DCERPC_Client, + key_handle: NDRContextHandle, + value_name: str, + ndr64: bool = True, + timeout: int = 5, + ) -> bool: + """ + Delete a given value from a registry key. + + :param client: The DCERPC client. + :param hKey: The handle to the subkey to remove. + :param value_name: The name of the value to delete. + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: True if the value was deleted successfully, False otherwise. + """ + + if not str(value_name).endswith("\x00"): + value_name = str(value_name) + "\x00" + + response = client.sr1_req( + BaseRegDeleteValue_Request( + hKey=key_handle, + lpValueName=RPC_UNICODE_STRING(Buffer=value_name), + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error( + "Got status %s while deleting value %s", + hex(response.status), + value_name, + ) + raise ValueError(response.status) + + return True + + @staticmethod + def close_key( + client: DCERPC_Client, + key_handle: NDRContextHandle, + ndr64: bool = True, + timeout: int = 5, + ) -> bool: + """ + Close a given registry key handle. + + :param client: The DCERPC client. + :param hKey: The handle to the registry key (root key or subkey). + :param ndr64: Whether to use NDR64 encoding. + :param timeout: The timeout for the request. + :return: True if the key was closed successfully, False otherwise. + """ + + response = client.sr1_req( + BaseRegCloseKey_Request( + hKey=key_handle, + ndr64=ndr64, + ), + timeout=timeout, + ) + + if not RegApi.is_status_ok(response.status): + log_runtime.error("Got status %s while closing key", hex(response.status)) + raise ValueError(response.status) + + return True diff --git a/scapy/layers/windows/security.py b/scapy/layers/windows/security.py new file mode 100644 index 00000000000..555df03a749 --- /dev/null +++ b/scapy/layers/windows/security.py @@ -0,0 +1,936 @@ +# SPDX-License-Identifier: GPL-2.0-only +# This file is part of Scapy +# See https://scapy.net/ for more information +# Copyright (C) Gabriel Potter +# pylint: disable=invalid-name + +""" +Python objects for Microsoft Windows security structures. +""" + +from dataclasses import dataclass +import re +import struct + +from scapy.config import conf +from scapy.packet import Packet, bind_layers +from scapy.fields import ( + ByteEnumField, + ByteField, + ConditionalField, + FieldLenField, + FieldListField, + FlagValue, + FlagsField, + LEIntField, + LELongField, + LenField, + LEShortEnumField, + LEShortField, + MultipleTypeField, + PacketField, + PacketListField, + ShortField, + StrFieldUtf16, + StrFixedLenField, + StrLenField, + StrLenFieldUtf16, + UUIDField, +) + +from scapy.layers.ntlm import ( + _NTLMPayloadField, + _NTLMPayloadPacket, + _NTLM_ENUM, + _NTLM_post_build, +) + +# [MS-DTYP] sect 2.4.1 + + +class WINNT_SID_IDENTIFIER_AUTHORITY(Packet): + """ + Security Identifier (SID) Identifier Authority + Standard values are: + - SECURITY_NULL_SID_AUTHORITY 0 S-1-0 + - SECURITY_WORLD_SID_AUTHORITY 1 S-1-1 + - SECURITY_LOCAL_SID_AUTHORITY 2 S-1-2 + - SECURITY_CREATOR_SID_AUTHORITY 3 S-1-3 + - NON_UNIQUE_AUTHORITY 4 S-1-4 + - SECURITY_NT_AUTHORITY 5 S-1-5 + - SECURITY_APP_PACKAGE_AUTHORITY 15 S-1-15 + - SECURITY_MANDATORY_LABEL_AUTHORITY 16 S-1-16 + - SECURITY_SCOPED_POLICY_ID_AUTHORITY 17 S-1-17 + - SECURITY_AUTHENTICATION_AUTHORITY 18 S-1-18 + """ + + fields_desc: list[StrFixedLenField] = [ + StrFixedLenField("Value", b"\x00\x00\x00\x00\x00\x01", length=6), + ] + + def default_payload_class(self, payload: bytes) -> Packet: + return conf.padding_layer + + +# [MS-DTYP] sect 2.4.2 + + +class WINNT_SID(Packet): + """Complete Security Identifier (SID) structure""" + + fields_desc = [ + ByteField("Revision", 1), + FieldLenField("SubAuthorityCount", None, count_of="SubAuthority", fmt="B"), + PacketField( + "IdentifierAuthority", + WINNT_SID_IDENTIFIER_AUTHORITY(), + WINNT_SID_IDENTIFIER_AUTHORITY, + ), + FieldListField( + "SubAuthority", + [0], + LEIntField("", 0), + count_from=lambda pkt: pkt.SubAuthorityCount, + ), + ] + + def default_payload_class(self, payload: bytes) -> Packet: + return conf.padding_layer + + _SID_REG = re.compile(r"^S-(\d)-(\d+)((?:-\d+)*)$") + + @staticmethod + def fromstr(x: str): + """ + Helper to create a SID from its string representation. + + :param x: string representation of the SID like "S-1-5-18" + :type x: str + + .. code-block:: python + >>> from scapy.layers.win_security import WINNT_SID + >>> WINNT_SID.fromstr("S-1-5-18") + SubAuthority=[18] |> + >>> _.summary() + >>> 'S-1-5-18' + """ + + m = WINNT_SID._SID_REG.match(x) + if not m: + raise ValueError("Invalid SID format !") + rev, authority, subauthority = m.groups() + return WINNT_SID( + Revision=int(rev), + IdentifierAuthority=WINNT_SID_IDENTIFIER_AUTHORITY( + Value=struct.pack(">Q", int(authority))[2:] + ), + SubAuthority=[int(x) for x in subauthority[1:].split("-")], + ) + + def summary(self) -> str: + """ + Return the string representation of the SID. + """ + return "S-%s-%s%s" % ( + self.Revision, + struct.unpack(">Q", b"\x00\x00" + self.IdentifierAuthority.Value)[0], + ( + ("-%s" % "-".join(str(x) for x in self.SubAuthority)) + if self.SubAuthority + else "" + ), + ) + + +# https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers + +# pylint: disable-next=duplicate-key +WELL_KNOWN_SIDS = { + # Universal well-known SID + "S-1-0-0": "Null SID", + "S-1-1-0": "Everyone", + "S-1-2-0": "Local", + "S-1-2-1": "Console Logon", + "S-1-3-0": "Creator Owner ID", + "S-1-3-1": "Creator Group ID", + "S-1-3-2": "Owner Server", + "S-1-3-3": "Group Server", + "S-1-3-4": "Owner Rights", + "S-1-4": "Non-unique Authority", + "S-1-5": "NT Authority", + "S-1-5-80-0": "All Services", + # NT well-known SIDs + "S-1-5-1": "Dialup", + "S-1-5-113": "Local account", + "S-1-5-114": "Local account and member of Administrators group", + "S-1-5-2": "Network", + "S-1-5-3": "Batch", + "S-1-5-4": "Interactive", + "S-1-5-6": "Service", + "S-1-5-7": "Anonymous Logon", + "S-1-5-8": "Proxy", + "S-1-5-9": "Enterprise Domain Controllers", + "S-1-5-10": "Self", + "S-1-5-11": "Authenticated Users", + "S-1-5-12": "Restricted Code", + "S-1-5-13": "Terminal Server User", + "S-1-5-14": "Remote Interactive Logon", + "S-1-5-15": "This Organization", + "S-1-5-17": "IUSR", + "S-1-5-18": "System (or LocalSystem)", + "S-1-5-19": "NT Authority (LocalService)", + "S-1-5-20": "Network Service", + "S-1-5-32-544": "Administrators", + "S-1-5-32-545": "Users", + "S-1-5-32-546": "Guests", + "S-1-5-32-547": "Power Users", + "S-1-5-32-548": "Account Operators", + "S-1-5-32-549": "Server Operators", + "S-1-5-32-550": "Print Operators", + "S-1-5-32-551": "Backup Operators", + "S-1-5-32-552": "Replicators", + "S-1-5-32-554": r"Builtin\Pre-Windows 2000 Compatible Access", + "S-1-5-32-555": r"Builtin\Remote Desktop Users", + "S-1-5-32-556": r"Builtin\Network Configuration Operators", + "S-1-5-32-557": r"Builtin\Incoming Forest Trust Builders", + "S-1-5-32-558": r"Builtin\Performance Monitor Users", + "S-1-5-32-559": r"Builtin\Performance Log Users", + "S-1-5-32-560": r"Builtin\Windows Authorization Access Group", + "S-1-5-32-561": r"Builtin\Terminal Server License Servers", + "S-1-5-32-562": r"Builtin\Distributed COM Users", + "S-1-5-32-568": r"Builtin\IIS_IUSRS", + "S-1-5-32-569": r"Builtin\Cryptographic Operators", + "S-1-5-32-573": r"Builtin\Event Log Readers", + "S-1-5-32-574": r"Builtin\Certificate Service DCOM Access", + "S-1-5-32-575": r"Builtin\RDS Remote Access Servers", + "S-1-5-32-576": r"Builtin\RDS Endpoint Servers", + "S-1-5-32-577": r"Builtin\RDS Management Servers", + "S-1-5-32-578": r"Builtin\Hyper-V Administrators", + "S-1-5-32-579": r"Builtin\Access Control Assistance Operators", + "S-1-5-32-580": r"Builtin\Remote Management Users", + "S-1-5-32-581": r"Builtin\Default Account", + "S-1-5-32-582": r"Builtin\Storage Replica Admins", + "S-1-5-32-583": r"Builtin\Device Owners", + "S-1-5-64-10": "NTLM Authentication", + "S-1-5-64-14": "SChannel Authentication", + "S-1-5-64-21": "Digest Authentication", + "S-1-5-80": "NT Service", + "S-1-5-80-0": "All Services", + "S-1-5-83-0": r"NT VIRTUAL MACHINE\Virtual Machines", +} + + +# [MS-DTYP] sect 2.4.3 + +_WINNT_ACCESS_MASK = { + 0x80000000: "GENERIC_READ", + 0x40000000: "GENERIC_WRITE", + 0x20000000: "GENERIC_EXECUTE", + 0x10000000: "GENERIC_ALL", + 0x02000000: "MAXIMUM_ALLOWED", + 0x01000000: "ACCESS_SYSTEM_SECURITY", + 0x00100000: "SYNCHRONIZE", + 0x00080000: "WRITE_OWNER", + 0x00040000: "WRITE_DACL", + 0x00020000: "READ_CONTROL", + 0x00010000: "DELETE", +} + + +# [MS-DTYP] sect 2.4.4.1 + + +WINNT_ACE_FLAGS = { + 0x01: "OBJECT_INHERIT", + 0x02: "CONTAINER_INHERIT", + 0x04: "NO_PROPAGATE_INHERIT", + 0x08: "INHERIT_ONLY", + 0x10: "INHERITED_ACE", + 0x40: "SUCCESSFUL_ACCESS", + 0x80: "FAILED_ACCESS", +} + + +class WINNT_ACE_HEADER(Packet): + """ + Access Control Entry (ACE) Header + It is composed of 3 fields, followed by ACE-specific data: + - AceType (1 byte): see below for standard values + - AceFlags (1 byte): see WINNT_ACE_FLAGS + - AceSize (2 bytes): total size of the ACE, including the header + and the ACE-specific data. + """ + + fields_desc = [ + ByteEnumField( + "AceType", + 0, + { + 0x00: "ACCESS_ALLOWED", + 0x01: "ACCESS_DENIED", + 0x02: "SYSTEM_AUDIT", + 0x03: "SYSTEM_ALARM", + 0x04: "ACCESS_ALLOWED_COMPOUND", + 0x05: "ACCESS_ALLOWED_OBJECT", + 0x06: "ACCESS_DENIED_OBJECT", + 0x07: "SYSTEM_AUDIT_OBJECT", + 0x08: "SYSTEM_ALARM_OBJECT", + 0x09: "ACCESS_ALLOWED_CALLBACK", + 0x0A: "ACCESS_DENIED_CALLBACK", + 0x0B: "ACCESS_ALLOWED_CALLBACK_OBJECT", + 0x0C: "ACCESS_DENIED_CALLBACK_OBJECT", + 0x0D: "SYSTEM_AUDIT_CALLBACK", + 0x0E: "SYSTEM_ALARM_CALLBACK", + 0x0F: "SYSTEM_AUDIT_CALLBACK_OBJECT", + 0x10: "SYSTEM_ALARM_CALLBACK_OBJECT", + 0x11: "SYSTEM_MANDATORY_LABEL", + 0x12: "SYSTEM_RESOURCE_ATTRIBUTE", + 0x13: "SYSTEM_SCOPED_POLICY_ID", + }, + ), + FlagsField( + "AceFlags", + 0, + 8, + WINNT_ACE_FLAGS, + ), + LenField("AceSize", None, fmt=" conditional expression + cond_expr = None + if hasattr(self.payload, "ApplicationData"): + # Parse tokens + res = [] + for ct in self.payload.ApplicationData.Tokens: + if ct.TokenType in [ + # binary operators + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x88, 0x8e, 0x8f, + 0xa0, 0xa1 + ]: + t1 = res.pop(-1) + t0 = res.pop(-1) + tt = ct.sprintf("%TokenType%") + if ct.TokenType in [0xa0, 0xa1]: # && and || + res.append(f"({t0}) {tt} ({t1})") + else: + res.append(f"{t0} {tt} {t1}") + elif ct.TokenType in [ + # unary operators + 0x87, 0x8d, 0xa2, 0x89, 0x8a, 0x8b, 0x8c, 0x91, 0x92, 0x93 + ]: + t0 = res.pop(-1) + tt = ct.sprintf("%TokenType%") + res.append(f"{tt}{t0}") + elif ct.TokenType in [ + # values + 0x01, 0x02, 0x03, 0x04, 0x10, 0x18, 0x50, 0x51, 0xf8, 0xf9, + 0xfa, 0xfb + ]: + def lit(ct): + if ct.TokenType in [0x10, 0x18]: # literal strings + return '"%s"' % ct.value + elif ct.TokenType == 0x50: # composite + return "({%s})" % ",".join(lit(x) for x in ct.value) + else: + return str(ct.value) + res.append(lit(ct)) + elif ct.TokenType == 0x00: # padding + pass + else: + raise ValueError("Unhandled token type %s" % ct.TokenType) + if len(res) != 1: + raise ValueError("Incomplete SDDL !") + cond_expr = "(%s)" % res[0] + return { + "ace-flags-string": ace_flag_string, + "sid-string": sid_string, + "mask": mask, + "object-guid": object_guid, + "inherited-object-guid": inherit_object_guid, + "cond-expr": cond_expr, + } + # fmt: on + + def toSDDL(self, accessMask=None): + """ + Return SDDL + """ + data = self.extractData(accessMask=accessMask) + ace_rights = "" # TODO + if self.AceType in [0x9, 0xA, 0xB, 0xD]: # Conditional ACE + conditional_ace_type = { + 0x09: "XA", + 0x0A: "XD", + 0x0B: "XU", + 0x0D: "ZA", + }[self.AceType] + return "D:(%s)" % ( + ";".join( + x + for x in [ + conditional_ace_type, + data["ace-flags-string"], + ace_rights, + str(data["object-guid"]), + str(data["inherited-object-guid"]), + data["sid-string"], + data["cond-expr"], + ] + if x is not None + ) + ) + else: + ace_type = { + 0x00: "A", + 0x01: "D", + 0x02: "AU", + 0x05: "OA", + 0x06: "OD", + 0x07: "OU", + 0x11: "ML", + 0x13: "SP", + }[self.AceType] + return "(%s)" % ( + ";".join( + x + for x in [ + ace_type, + data["ace-flags-string"], + ace_rights, + str(data["object-guid"]), + str(data["inherited-object-guid"]), + data["sid-string"], + data["cond-expr"], + ] + if x is not None + ) + ) + + +# [MS-DTYP] sect 2.4.4.2 + + +class WINNT_ACCESS_ALLOWED_ACE(Packet): + fields_desc = [ + FlagsField("Mask", 0, -32, _WINNT_ACCESS_MASK), + PacketField("Sid", WINNT_SID(), WINNT_SID), + ] + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_ACE, AceType=0x00) + + +# [MS-DTYP] sect 2.4.4.3 + + +class WINNT_ACCESS_ALLOWED_OBJECT_ACE(Packet): + fields_desc = [ + FlagsField("Mask", 0, -32, _WINNT_ACCESS_MASK), + FlagsField( + "Flags", + 0, + -32, + { + 0x00000001: "OBJECT_TYPE_PRESENT", + 0x00000002: "INHERITED_OBJECT_TYPE_PRESENT", + }, + ), + ConditionalField( + UUIDField("ObjectType", None, uuid_fmt=UUIDField.FORMAT_LE), + lambda pkt: pkt.Flags.OBJECT_TYPE_PRESENT, + ), + ConditionalField( + UUIDField("InheritedObjectType", None, uuid_fmt=UUIDField.FORMAT_LE), + lambda pkt: pkt.Flags.INHERITED_OBJECT_TYPE_PRESENT, + ), + PacketField("Sid", WINNT_SID(), WINNT_SID), + ] + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_OBJECT_ACE, AceType=0x05) + + +# [MS-DTYP] sect 2.4.4.4 + + +class WINNT_ACCESS_DENIED_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_ACE, AceType=0x01) + + +# [MS-DTYP] sect 2.4.4.5 + + +class WINNT_ACCESS_DENIED_OBJECT_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_OBJECT_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_OBJECT_ACE, AceType=0x06) + + +# [MS-DTYP] sect 2.4.4.17.4+ + + +class WINNT_APPLICATION_DATA_LITERAL_TOKEN(Packet): + def default_payload_class(self, payload): + return conf.padding_layer + + +# fmt: off +WINNT_APPLICATION_DATA_LITERAL_TOKEN.fields_desc = [ + ByteEnumField( + "TokenType", + 0, + { + # [MS-DTYP] sect 2.4.4.17.5 + 0x00: "Padding token", + 0x01: "Signed int8", + 0x02: "Signed int16", + 0x03: "Signed int32", + 0x04: "Signed int64", + 0x10: "Unicode", + 0x18: "Octet String", + 0x50: "Composite", + 0x51: "SID", + # [MS-DTYP] sect 2.4.4.17.6 + 0x80: "==", + 0x81: "!=", + 0x82: "<", + 0x83: "<=", + 0x84: ">", + 0x85: ">=", + 0x86: "Contains", + 0x88: "Any_of", + 0x8e: "Not_Contains", + 0x8f: "Not_Any_of", + 0x89: "Member_of", + 0x8a: "Device_Member_of", + 0x8b: "Member_of_Any", + 0x8c: "Device_Member_of_Any", + 0x90: "Not_Member_of", + 0x91: "Not_Device_Member_of", + 0x92: "Not_Member_of_Any", + 0x93: "Not_Device_Member_of_Any", + # [MS-DTYP] sect 2.4.4.17.7 + 0x87: "Exists", + 0x8d: "Not_Exists", + 0xa0: "&&", + 0xa1: "||", + 0xa2: "!", + # [MS-DTYP] sect 2.4.4.17.8 + 0xf8: "Local attribute", + 0xf9: "User Attribute", + 0xfa: "Resource Attribute", + 0xfb: "Device Attribute", + } + ), + ConditionalField( + # Strings + LEIntField("length", 0), + lambda pkt: pkt.TokenType in [ + 0x10, # Unicode string + 0x18, # Octet string + 0xf8, 0xf9, 0xfa, 0xfb, # Attribute tokens + 0x50, # Composite + ] + ), + ConditionalField( + MultipleTypeField( + [ + ( + LELongField("value", 0), + lambda pkt: pkt.TokenType in [ + 0x01, # signed int8 + 0x02, # signed int16 + 0x03, # signed int32 + 0x04, # signed int64 + ] + ), + ( + StrLenFieldUtf16("value", b"", length_from=lambda pkt: pkt.length), + lambda pkt: pkt.TokenType in [ + 0x10, # Unicode string + 0xf8, 0xf9, 0xfa, 0xfb, # Attribute tokens + ] + ), + ( + StrLenField("value", b"", length_from=lambda pkt: pkt.length), + lambda pkt: pkt.TokenType == 0x18, # Octet string + ), + ( + PacketListField("value", [], WINNT_APPLICATION_DATA_LITERAL_TOKEN, + length_from=lambda pkt: pkt.length), + lambda pkt: pkt.TokenType == 0x50, # Composite + ), + + ], + StrFixedLenField("value", b"", length=0), + ), + lambda pkt: pkt.TokenType in [ + 0x01, 0x02, 0x03, 0x04, 0x10, 0x18, 0xf8, 0xf9, 0xfa, 0xfb, 0x50 + ] + ), + ConditionalField( + # Literal + ByteEnumField("sign", 0, { + 0x01: "+", + 0x02: "-", + 0x03: "None", + }), + lambda pkt: pkt.TokenType in [ + 0x01, # signed int8 + 0x02, # signed int16 + 0x03, # signed int32 + 0x04, # signed int64 + ] + ), + ConditionalField( + # Literal + ByteEnumField("base", 0, { + 0x01: "Octal", + 0x02: "Decimal", + 0x03: "Hexadecimal", + }), + lambda pkt: pkt.TokenType in [ + 0x01, # signed int8 + 0x02, # signed int16 + 0x03, # signed int32 + 0x04, # signed int64 + ] + ), +] +# fmt: on + + +class WINNT_APPLICATION_DATA(Packet): + fields_desc = [ + StrFixedLenField("Magic", b"\x61\x72\x74\x78", length=4), + PacketListField( + "Tokens", + [], + WINNT_APPLICATION_DATA_LITERAL_TOKEN, + ), + ] + + def default_payload_class(self, payload): + return conf.padding_layer + + +# [MS-DTYP] sect 2.4.4.6 + + +class WINNT_ACCESS_ALLOWED_CALLBACK_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc + [ + PacketField( + "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA + ), + ] + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_CALLBACK_ACE, AceType=0x09) + + +# [MS-DTYP] sect 2.4.4.7 + + +class WINNT_ACCESS_DENIED_CALLBACK_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_CALLBACK_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_CALLBACK_ACE, AceType=0x0A) + + +# [MS-DTYP] sect 2.4.4.8 + + +class WINNT_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_OBJECT_ACE.fields_desc + [ + PacketField( + "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA + ), + ] + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE, AceType=0x0B) + + +# [MS-DTYP] sect 2.4.4.9 + + +class WINNT_ACCESS_DENIED_CALLBACK_OBJECT_ACE(Packet): + fields_desc = WINNT_ACCESS_DENIED_OBJECT_ACE.fields_desc + [ + PacketField( + "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA + ), + ] + + +bind_layers(WINNT_ACE_HEADER, WINNT_ACCESS_DENIED_CALLBACK_OBJECT_ACE, AceType=0x0C) + + +# [MS-DTYP] sect 2.4.4.10 + + +class WINNT_SYSTEM_AUDIT_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_ACE, AceType=0x02) + + +# [MS-DTYP] sect 2.4.4.11 + + +class WINNT_SYSTEM_AUDIT_OBJECT_ACE(Packet): + # doc is wrong. + fields_desc = WINNT_ACCESS_ALLOWED_OBJECT_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_OBJECT_ACE, AceType=0x07) + + +# [MS-DTYP] sect 2.4.4.12 + + +class WINNT_SYSTEM_AUDIT_CALLBACK_ACE(Packet): + fields_desc = WINNT_SYSTEM_AUDIT_ACE.fields_desc + [ + PacketField( + "ApplicationData", WINNT_APPLICATION_DATA(), WINNT_APPLICATION_DATA + ), + ] + + +bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_CALLBACK_ACE, AceType=0x0D) + + +# [MS-DTYP] sect 2.4.4.13 + + +class WINNT_SYSTEM_MANDATORY_LABEL_ACE(Packet): + fields_desc = WINNT_SYSTEM_AUDIT_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_MANDATORY_LABEL_ACE, AceType=0x11) + + +# [MS-DTYP] sect 2.4.4.14 + + +class WINNT_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE(Packet): + fields_desc = WINNT_SYSTEM_AUDIT_OBJECT_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE, AceType=0x0F) + +# [MS-DTYP] sect 2.4.10.1 + + +class CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1(_NTLMPayloadPacket): + _NTLM_PAYLOAD_FIELD_NAME = "Data" + fields_desc = [ + LEIntField("NameOffset", 0), + LEShortEnumField( + "ValueType", + 0, + { + 0x0001: "CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64", + 0x0002: "CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64", + 0x0003: "CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING", + 0x0005: "CLAIM_SECURITY_ATTRIBUTE_TYPE_SID", + 0x0006: "CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN", + 0x0010: "CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING", + }, + ), + LEShortField("Reserved", 0), + FlagsField( + "Flags", + 0, + -32, + { + 0x0001: "CLAIM_SECURITY_ATTRIBUTE_NON_INHERITABLE", + 0x0002: "CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE", + 0x0004: "CLAIM_SECURITY_ATTRIBUTE_USE_FOR_DENY_ONLY", + 0x0008: "CLAIM_SECURITY_ATTRIBUTE_DISABLED_BY_DEFAULT", + 0x0010: "CLAIM_SECURITY_ATTRIBUTE_DISABLED", + 0x0020: "CLAIM_SECURITY_ATTRIBUTE_MANDATORY", + }, + ), + LEIntField("ValueCount", 0), + FieldListField( + "ValueOffsets", [], LEIntField("", 0), count_from=lambda pkt: pkt.ValueCount + ), + _NTLMPayloadField( + "Data", + lambda pkt: 16 + pkt.ValueCount * 4, + [ + ConditionalField( + StrFieldUtf16("Name", b""), + lambda pkt: pkt.NameOffset, + ), + # TODO: Values + ], + offset_name="Offset", + ), + ] + + +# [MS-DTYP] sect 2.4.4.15 + + +class WINNT_SYSTEM_RESOURCE_ATTRIBUTE_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc + [ + PacketField( + "AttributeData", + CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1(), + CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, + ) + ] + + +bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_RESOURCE_ATTRIBUTE_ACE, AceType=0x12) + +# [MS-DTYP] sect 2.4.4.16 + + +class WINNT_SYSTEM_SCOPED_POLICY_ID_ACE(Packet): + fields_desc = WINNT_ACCESS_ALLOWED_ACE.fields_desc + + +bind_layers(WINNT_ACE_HEADER, WINNT_SYSTEM_SCOPED_POLICY_ID_ACE, AceType=0x13) + +# [MS-DTYP] sect 2.4.5 + + +class WINNT_ACL(Packet): + fields_desc = [ + ByteField("AclRevision", 2), + ByteField("Sbz1", 0x00), + # Total size including header: + # AclRevision(1) + Sbz1(1) + AclSize(2) + AceCount(2) + Sbz2(2) + FieldLenField( + "AclSize", + None, + length_of="Aces", + adjust=lambda _, x: x + 8, + fmt=" bytes + return ( + _NTLM_post_build( + self, + pkt, + self.OFFSET, + { + "OwnerSid": 4, + "GroupSid": 8, + "SACL": 12, + "DACL": 16, + }, + config=[ + ("Offset", _NTLM_ENUM.OFFSET), + ], + ) + + pay + ) + + +# High level access rights definitions diff --git a/scapy/modules/ldaphero.py b/scapy/modules/ldaphero.py index 9ade79eb5d9..b081a7ecbdf 100644 --- a/scapy/modules/ldaphero.py +++ b/scapy/modules/ldaphero.py @@ -43,7 +43,7 @@ from scapy.layers.ntlm import NTLMSSP from scapy.layers.kerberos import KerberosSSP from scapy.layers.spnego import SPNEGOSSP -from scapy.layers.smb2 import ( +from scapy.layers.windows.security import ( SECURITY_DESCRIPTOR, WELL_KNOWN_SIDS, WINNT_ACE_FLAGS, diff --git a/scapy/modules/ticketer.py b/scapy/modules/ticketer.py index 8f8e9bfdd6c..e301bc23248 100644 --- a/scapy/modules/ticketer.py +++ b/scapy/modules/ticketer.py @@ -98,7 +98,7 @@ USER_SESSION_KEY, CLAIM_ENTRY_sub2, ) -from scapy.layers.smb2 import ( +from scapy.layers.windows.security import ( WINNT_SID, WINNT_SID_IDENTIFIER_AUTHORITY, )