CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/proto/ms_dtyp.rb
Views: 11655
1
# -*- coding: binary -*-
2
3
require 'bindata'
4
require 'ruby_smb'
5
require 'rex/proto/secauthz/well_known_sids'
6
7
module Rex::Proto::MsDtyp
8
# [2.4.3 ACCESS_MASK](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/7a53f60e-e730-4dfe-bbe9-b21b62eb790b)
9
class MsDtypAccessMask < BinData::Record
10
endian :little
11
hide :reserved0, :reserved1
12
13
# the protocol field id reserved for protocol-specific access rights
14
uint16 :protocol
15
16
bit3 :reserved0
17
bit1 :sy
18
bit1 :wo
19
bit1 :wd
20
bit1 :rc
21
bit1 :de
22
23
bit1 :gr
24
bit1 :gw
25
bit1 :gx
26
bit1 :ga
27
bit2 :reserved1
28
bit1 :ma
29
bit1 :as
30
def bit_names
31
names = []
32
names << :GENERIC_READ if self.gr != 0
33
names << :GENERIC_WRITE if self.gw != 0
34
names << :GENERIC_EXECUTE if self.gx != 0
35
names << :GENERIC_ALL if self.ga != 0
36
names << :MAXIMUM_ALLOWED if self.ma != 0
37
names << :ACCESS_SYSTEM_SECURITY if self.as != 0
38
names << :SYNCHRONIZE if self.sy != 0
39
names << :WRITE_OWNER if self.wo != 0
40
names << :WRITE_DACL if self.wd != 0
41
names << :READ_CONTROL if self.rc != 0
42
names << :DELETE if self.de != 0
43
names
44
end
45
46
ALL = MsDtypAccessMask.new({ gr: 1, gw: 1, gx: 1, ga: 1, ma: 1, as: 1, sy: 1, wo: 1, wd: 1, rc: 1, de: 1, protocol: 0xffff })
47
NONE = MsDtypAccessMask.new({ gr: 0, gw: 0, gx: 0, ga: 0, ma: 0, as: 0, sy: 0, wo: 0, wd: 0, rc: 0, de: 0, protocol: 0 })
48
end
49
50
# [2.4.2.2 SID--Packet Representation](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/f992ad60-0fe4-4b87-9fed-beb478836861)
51
class MsDtypSid < BinData::Primitive
52
endian :little
53
54
uint8 :revision, initial_value: 1
55
uint8 :sub_authority_count, initial_value: -> { self.sub_authority.length }
56
array :identifier_authority, type: :uint8, initial_length: 6
57
array :sub_authority, type: :uint32, initial_length: :sub_authority_count
58
59
def set(val)
60
# allow assignment from the human-readable string representation
61
raise ArgumentError.new("Invalid SID: #{val}") unless val.is_a?(String) && val =~ /^S-1-(\d+)(-\d+)*$/
62
63
_, _, ia, sa = val.split('-', 4)
64
self.identifier_authority = [ia.to_i].pack('Q>')[2..].bytes
65
self.sub_authority = sa.nil? ? [] : sa.split('-').map(&:to_i)
66
end
67
68
def get
69
str = 'S-1'
70
str << "-#{("\x00\x00" + identifier_authority.to_binary_s).unpack1('Q>')}"
71
str << '-' + sub_authority.map(&:to_s).join('-') unless sub_authority.empty?
72
str
73
end
74
75
def rid
76
sub_authority.last
77
end
78
end
79
80
# [Universal Unique Identifier](http://pubs.opengroup.org/onlinepubs/9629399/apdxa.htm)
81
# The online documentation at [2.3.4.2](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/001eec5a-7f8b-4293-9e21-ca349392db40)
82
# weirdly doesn't mention this needs to be 4 byte aligned for us to read it correctly,
83
# which the RubySMB::Dcerpc::Uuid definition takes care of.
84
class MsDtypGuid < RubySMB::Dcerpc::Uuid
85
def self.random_generate
86
# Taken from the "D" format as specified in
87
# https://learn.microsoft.com/en-us/dotnet/api/system.guid.tostring?view=net-7.0
88
"{#{Rex::Text.rand_text_hex(8)}-#{Rex::Text.rand_text_hex(4)}-#{Rex::Text.rand_text_hex(4)}-#{Rex::Text.rand_text_hex(4)}-#{Rex::Text.rand_text_hex(12)}}".downcase
89
end
90
end
91
92
# Definitions taken from [2.4.4.1 ACE_HEADER](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/628ebb1d-c509-4ea0-a10f-77ef97ca4586)
93
class MsDtypAceType
94
ACCESS_ALLOWED_ACE_TYPE = 0x0
95
ACCESS_DENIED_ACE_TYPE = 0x1
96
SYSTEM_AUDIT_ACE_TYPE = 0x2
97
SYSTEM_ALARM_ACE_TYPE = 0x3 # Reserved for future use according to documentation.
98
ACCESS_ALLOWED_COMPOUND_ACE_TYPE = 0x4 # Reserved for future use according to documentation.
99
ACCESS_ALLOWED_OBJECT_ACE_TYPE = 0x5
100
ACCESS_DENIED_OBJECT_ACE_TYPE = 0x6
101
SYSTEM_AUDIT_OBJECT_ACE_TYPE = 0x7
102
SYSTEM_ALARM_OBJECT_ACE_TYPE = 0x8 # Reserved for future use according to documentation.
103
ACCESS_ALLOWED_CALLBACK_ACE_TYPE = 0x9
104
ACCESS_DENIED_CALLBACK_ACE_TYPE = 0xA
105
ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 0xB
106
ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = 0xC
107
SYSTEM_AUDIT_CALLBACK_ACE_TYPE = 0xD
108
SYSTEM_ALARM_CALLBACK_ACE_TYPE = 0xE # Reserved for future use according to documentation.
109
SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = 0xF
110
SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = 0x10 # Reserved for future use according to documentation.
111
SYSTEM_MANDATORY_LABEL_ACE_TYPE = 0x11
112
SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE = 0x12
113
SYSTEM_SCOPED_POLICY_ID_ACE_TYPE = 0x13
114
115
def self.name(value)
116
constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
117
end
118
end
119
120
# [2.4.4.1 ACE_HEADER](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/628ebb1d-c509-4ea0-a10f-77ef97ca4586)
121
class MsDtypAceHeader < BinData::Record
122
endian :little
123
124
uint8 :ace_type
125
struct :ace_flags do
126
bit1 :failed_access_ace_flag
127
bit1 :successful_access_ace_flag
128
bit1 :critical_ace_flag # used only with access allowed ACE types, see: https://www.codemachine.com/downloads/win10.1903/ntifs.h
129
bit1 :inherited_ace
130
bit1 :inherit_only_ace
131
bit1 :no_propagate_inherit_ace
132
bit1 :container_inherit_ace
133
bit1 :object_inherit_ace
134
end
135
uint16 :ace_size, initial_value: -> { parent&.num_bytes || 0 }
136
end
137
138
class MsDtypAceNonObjectBody < BinData::Record
139
endian :little
140
141
ms_dtyp_access_mask :access_mask
142
ms_dtyp_sid :sid, byte_align: 4
143
end
144
145
class MsDtypAceObjectBody < BinData::Record
146
endian :little
147
148
ms_dtyp_access_mask :access_mask
149
struct :flags do
150
bit1 :reserved5
151
bit1 :reserved4
152
bit1 :reserved3
153
bit1 :reserved2
154
bit1 :reserved1
155
bit1 :reserved
156
bit1 :ace_inherited_object_type_present
157
bit1 :ace_object_type_present
158
end
159
ms_dtyp_guid :object_type, onlyif: -> { flags.ace_object_type_present != 0x0 }
160
ms_dtyp_guid :inherited_object_type, onlyif: -> { flags.ace_inherited_object_type_present != 0x0 }
161
ms_dtyp_sid :sid, byte_align: 4
162
end
163
164
# [2.4.4.2 ACCESS_ALLOWED_ACE](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/72e7c7ea-bc02-4c74-a619-818a16bf6adb)
165
class MsDtypAccessAllowedAceBody < MsDtypAceNonObjectBody
166
end
167
168
# [2.4.4.4 ACCESS_DENIED_ACE](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/b1e1321d-5816-4513-be67-b65d8ae52fe8)
169
class MsDtypAccessDeniedAceBody < MsDtypAceNonObjectBody
170
end
171
172
# [2.4.4.10 SYSTEM_AUDIT_ACE](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/9431fd0f-5b9a-47f0-b3f0-3015e2d0d4f9)
173
class MsDtypSystemAuditAceBody < MsDtypAceNonObjectBody
174
end
175
176
# [2.4.4.3 ACCESS_ALLOWED_OBJECT_ACE](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/c79a383c-2b3f-4655-abe7-dcbb7ce0cfbe)
177
class MsDtypAccessAllowedObjectAceBody < MsDtypAceObjectBody
178
end
179
180
# [2.4.4.5 ACCESS_DENIED_OBJECT_ACE](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/8720fcf3-865c-4557-97b1-0b3489a6c270)
181
class MsDtypAccessDeniedObjectAceBody < MsDtypAceObjectBody
182
end
183
184
# [2.4.4.11 SYSTEM_AUDIT_OBJECT_ACE](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/c8da72ae-6b54-4a05-85f4-e2594936d3d5)
185
class MsDtypSystemAuditObjectAceBody < MsDtypAceObjectBody
186
endian :little
187
188
string :application_data, read_length: -> { calc_app_data_length }
189
190
def calc_app_data_length
191
ace_header = parent&.header
192
return 0 if ace_header.nil?
193
ace_size = ace_header&.ace_size
194
return 0 if ace_size.nil? or (ace_size == 0)
195
196
ace_header_length = ace_header.to_binary_s.length
197
body = parent&.body
198
if body.nil?
199
return 0 # Read no data as there is no body, so either we have done some data misalignment or we shouldn't be reading data.
200
else
201
ace_body_length = body.to_binary_s.length
202
return ace_size - (ace_header_length + ace_body_length)
203
end
204
end
205
end
206
207
class MsDtypAce < BinData::Record
208
endian :little
209
210
ms_dtyp_ace_header :header
211
choice :body, selection: -> { header.ace_type } do
212
ms_dtyp_access_allowed_ace_body Rex::Proto::MsDtyp::MsDtypAceType::ACCESS_ALLOWED_ACE_TYPE
213
ms_dtyp_access_denied_ace_body Rex::Proto::MsDtyp::MsDtypAceType::ACCESS_DENIED_ACE_TYPE
214
ms_dtyp_system_audit_ace_body Rex::Proto::MsDtyp::MsDtypAceType::SYSTEM_AUDIT_ACE_TYPE
215
# Type 3 is reserved for future use
216
# Type 4 is reserved for future use
217
ms_dtyp_access_allowed_object_ace_body Rex::Proto::MsDtyp::MsDtypAceType::ACCESS_ALLOWED_OBJECT_ACE_TYPE
218
ms_dtyp_access_denied_object_ace_body Rex::Proto::MsDtyp::MsDtypAceType::ACCESS_DENIED_OBJECT_ACE_TYPE
219
ms_dtyp_system_audit_object_ace_body Rex::Proto::MsDtyp::MsDtypAceType::SYSTEM_AUDIT_OBJECT_ACE_TYPE
220
# Type 8 is reserved for future use
221
# Type 14 aka 0xE is reserved for future use
222
# Type 16 aka 0x10 is reserved for future use
223
string :default, read_length: -> { header.ace_size - body.rel_offset }
224
end
225
end
226
227
# [2.4.5 ACL](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/20233ed8-a6c6-4097-aafa-dd545ed24428)
228
class MsDtypAcl < BinData::Record
229
ACL_REVISION = 2
230
ACL_REVISION_DS = 4
231
232
endian :little
233
234
uint8 :acl_revision, initial_value: ACL_REVISION
235
uint8 :sbz1
236
uint16 :acl_size, initial_value: -> { num_bytes }
237
uint16 :acl_count, initial_value: -> { aces.length }
238
uint16 :sbz2
239
array :aces, type: :ms_dtyp_ace, initial_length: :acl_count
240
end
241
242
# [2.4.6 SECURITY_DESCRIPTOR](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/7d4dac05-9cef-4563-a058-f108abecce1d)
243
class MsDtypSecurityDescriptor < BinData::Record
244
endian :little
245
246
uint8 :revision, initial_value: 1
247
uint8 :sbz1
248
struct :control do
249
bit1 :ss
250
bit1 :dt
251
bit1 :sd
252
bit1 :sp, initial_value: -> { sacl? ? 1 : 0 }
253
bit1 :dd
254
bit1 :dp, initial_value: -> { dacl? ? 1 : 0 }
255
bit1 :gd
256
bit1 :od
257
258
bit1 :sr, initial_value: 1
259
bit1 :rm
260
bit1 :ps
261
bit1 :pd
262
bit1 :si
263
bit1 :di
264
bit1 :sc
265
bit1 :dc
266
end
267
uint32 :offset_owner, value: -> { offset_for(:owner_sid) }
268
uint32 :offset_group, value: -> { offset_for(:group_sid) }
269
uint32 :offset_sacl, value: -> { offset_for(:sacl) }
270
uint32 :offset_dacl, value: -> { offset_for(:dacl) }
271
rest :buffer, value: -> { build_buffer }
272
hide :buffer
273
274
def self.from_sddl_text(sddl_text, domain_sid:)
275
sacl_set = dacl_set = false
276
sd = self.new
277
sddl_text = sddl_text.dup.gsub(/\s/, '') # start by removing all whitespace
278
sddl_text.scan(/([OGDS]:(?:.(?!:))*)/).each do |part,|
279
component, _, value = part.partition(':')
280
case component
281
when 'O'
282
if sd.owner_sid.present?
283
raise RuntimeError.new('SDDL parse error on extra owner SID')
284
end
285
286
sd.owner_sid = self.parse_sddl_sid(value, domain_sid: domain_sid)
287
when 'G'
288
if sd.group_sid.present?
289
raise RuntimeError.new('SDDL parse error on extra group SID')
290
end
291
292
sd.group_sid = self.parse_sddl_sid(value, domain_sid: domain_sid)
293
when 'D'
294
raise RuntimeError.new('SDDL parse error on extra DACL') if dacl_set
295
296
value.upcase!
297
dacl_set = true
298
access_control = true
299
flags = value.split('(', 2).first || ''
300
flags.split(/(P|AR|AI|NO_ACCESS_CONTROL)/).each do |flag|
301
case flag
302
when 'AI'
303
sd.control.di = true
304
when 'AR'
305
sd.control.dc = true
306
when 'P'
307
sd.control.pd = true
308
when 'NO_ACCESS_CONTROL'
309
access_control = false
310
when ''
311
else
312
raise RuntimeError.new('SDDL parse error on unknown DACL flag: ' + flag)
313
end
314
end
315
316
next unless access_control
317
318
sd.dacl = MsDtypAcl.new
319
sd.dacl.aces = self.parse_sddl_aces(value.delete_prefix(flags), domain_sid: domain_sid)
320
when 'S'
321
raise RuntimeError.new('SDDL parse error on extra SACL') if sacl_set
322
323
value.upcase!
324
sacl_set = true
325
access_control = true
326
flags = value.split('(', 2).first || ''
327
flags.split(/(P|AR|AI|NO_ACCESS_CONTROL)/).each do |flag|
328
case flag
329
when 'AI'
330
sd.control.si = true
331
when 'AR'
332
sd.control.sc = true
333
when 'P'
334
sd.control.ps = true
335
when 'NO_ACCESS_CONTROL'
336
access_control = false
337
when ''
338
else
339
raise RuntimeError.new('SDDL parse error on unknown SACL flag: ' + flag)
340
end
341
end
342
343
next unless access_control
344
345
sd.sacl = MsDtypAcl.new
346
sd.sacl.aces = self.parse_sddl_aces(value.delete_prefix(flags), domain_sid: domain_sid)
347
else
348
raise RuntimeError.new('SDDL parse error on unknown directive: ' + part[0])
349
end
350
end
351
352
sd
353
end
354
355
class << self
356
private
357
358
def parse_sddl_ace(ace, domain_sid:)
359
parts = ace.upcase.split(';', -1)
360
raise RuntimeError.new('SDDL parse error on too few ACE fields') if parts.length < 6
361
raise RuntimeError.new('SDDL parse error on too many ACE fields') if parts.length > 7
362
363
ace_type, ace_flags, rights, object_guid, inherit_object_guid, account_sid = parts[0...6]
364
resource_attribute = parts[6]
365
366
ace = MsDtypAce.new
367
case ace_type
368
when 'A'
369
ace.header.ace_type = MsDtypAceType::ACCESS_ALLOWED_ACE_TYPE
370
when 'D'
371
ace.header.ace_type = MsDtypAceType::ACCESS_DENIED_ACE_TYPE
372
when 'OA'
373
ace.header.ace_type = MsDtypAceType::ACCESS_ALLOWED_OBJECT_ACE_TYPE
374
when 'OD'
375
ace.header.ace_type = MsDtypAceType::ACCESS_DENIED_OBJECT_ACE_TYPE
376
when 'AU'
377
ace.header.ace_type = MsDtypAceType::SYSTEM_AUDIT_ACE_TYPE
378
when 'OU'
379
ace.header.ace_type = MsDtypAceType::SYSTEM_AUDIT_OBJECT_ACE_TYPE
380
when 'AL', 'OL', 'ML', 'XA', 'SD', 'RA', 'SP', 'XU', 'ZA', 'TL', 'FL'
381
raise RuntimeError.new('SDDL parse error on unsupported ACE type: ' + ace_type)
382
else
383
raise RuntimeError.new('SDDL parse error on unknown ACE type: ' + ace_type)
384
end
385
386
ace_flags.split(/(CI|OI|NP|IO|ID|SA|FA|TP|CR)/).each do |flag|
387
case flag
388
when 'CI'
389
ace.header.ace_flags.container_inherit_ace = true
390
when 'OI'
391
ace.header.ace_flags.object_inherit_ace = true
392
when 'NP'
393
ace.header.ace_flags.no_propagate_inherit_ace = true
394
when 'IO'
395
ace.header.ace_flags.inherit_only_ace = true
396
when 'ID'
397
ace.header.ace_flags.inherited_ace = true
398
when 'SA'
399
ace.header.ace_flags.successful_access_ace_flag = true
400
when 'FA'
401
ace.header.ace_flags.failed_access_ace_flag = true
402
when 'TP'
403
raise RuntimeError.new('SDDL parse error on unsupported ACE flag: TP')
404
when 'CR'
405
ace.header.ace_flags.critical_ace_flag = true
406
when ''
407
else
408
raise RuntimeError.new('SDDL parse error on unknown ACE flag: ' + flag)
409
end
410
end
411
412
rights.split(/(G[ARWX]|RC|SD|WD|WO|RP|WP|CC|DC|LC|SW|LO|DT|CR|F[ARWX]|K[ARWX]|N[RWX])/).each do |right|
413
case right
414
# generic access rights
415
when 'GA', 'GR', 'GW', 'GX'
416
ace.body.access_mask.send("#{right.downcase}=", true)
417
# standard access rights
418
when 'RC'
419
ace.body.access_mask.rc = true
420
when 'SD'
421
ace.body.access_mask.de = true
422
when 'WD', 'WO'
423
ace.body.access_mask.send("#{right.downcase}=", true)
424
# directory service object access rights
425
when 'RP'
426
ace.body.access_mask.protocol |= 16
427
when 'WP'
428
ace.body.access_mask.protocol |= 32
429
when 'CC'
430
ace.body.access_mask.protocol |= 1
431
when 'DC'
432
ace.body.access_mask.protocol |= 2
433
when 'LC'
434
ace.body.access_mask.protocol |= 4
435
when 'SW'
436
ace.body.access_mask.protocol |= 8
437
when 'LO'
438
ace.body.access_mask.protocol |= 128
439
when 'DT'
440
ace.body.access_mask.protocol |= 64
441
when 'CR'
442
ace.body.access_mask.protocol |= 256
443
# file access rights
444
when 'FA'
445
ace.body.access_mask.protocol |= 0x1ff
446
ace.body.access_mask.de = true
447
ace.body.access_mask.rc = true
448
ace.body.access_mask.wd = true
449
ace.body.access_mask.wo = true
450
ace.body.access_mask.sy = true
451
when 'FR'
452
ace.body.access_mask.protocol |= 0x89
453
when 'FW'
454
ace.body.access_mask.protocol |= 0x116
455
when 'FX'
456
ace.body.access_mask.protocol |= 0xa0
457
# registry key access rights
458
when 'KA'
459
ace.body.access_mask.protocol |= 0x3f
460
ace.body.access_mask.de = true
461
ace.body.access_mask.rc = true
462
ace.body.access_mask.wd = true
463
ace.body.access_mask.wo = true
464
when 'KR'
465
ace.body.access_mask.protocol |= 0x19
466
when 'KW'
467
ace.body.access_mask.protocol |= 0x06
468
when 'KX'
469
ace.body.access_mask.protocol |= 0x19
470
when 'NR', 'NW', 'NX'
471
raise RuntimeError.new('SDDL parse error on unsupported ACE access right: ' + right)
472
when ''
473
else
474
raise RuntimeError.new('SDDL parse error on unknown ACE access right: ' + right)
475
end
476
end
477
478
unless object_guid.blank?
479
begin
480
guid = MsDtypGuid.new(object_guid)
481
rescue StandardError
482
raise RuntimeError.new('SDDL parse error on invalid object GUID: ' + object_guid)
483
end
484
485
unless ace.body.respond_to?('object_type=')
486
raise RuntimeError.new('SDDL error on setting object type for incompatible ACE type')
487
end
488
ace.body.flags.ace_object_type_present = true
489
ace.body.object_type = guid
490
end
491
492
unless inherit_object_guid.blank?
493
begin
494
guid = MsDtypGuid.new(inherit_object_guid)
495
rescue StandardError
496
raise RuntimeError.new('SDDL parse error on invalid object GUID: ' + inherit_object_guid)
497
end
498
499
unless ace.body.respond_to?('inherited_object_type=')
500
raise RuntimeError.new('SDDL error on setting object type for incompatible ACE type')
501
end
502
ace.body.flags.ace_inherited_object_type_present = true
503
ace.body.inherited_object_type = guid
504
end
505
506
unless account_sid.blank?
507
ace.body.sid = self.parse_sddl_sid(account_sid, domain_sid: domain_sid)
508
end
509
510
unless resource_attribute.blank?
511
raise RuntimeError.new('SDDL parse error on unsupported resource attribute: ' + resource_attribute)
512
end
513
514
ace
515
end
516
517
def parse_sddl_aces(aces, domain_sid:)
518
ace_regex = /\([^\)]*\)/
519
520
invalid_aces = aces.split(ace_regex).reject(&:empty?)
521
unless invalid_aces.empty?
522
raise RuntimeError.new('SDDL parse error on malformed ACE: ' + invalid_aces.first)
523
end
524
525
aces.scan(ace_regex).map do |ace_text|
526
self.parse_sddl_ace(ace_text[1...-1], domain_sid: domain_sid)
527
end
528
end
529
530
def parse_sddl_sid(sid, domain_sid:)
531
# see: https://learn.microsoft.com/en-us/windows/win32/secauthz/sid-strings
532
sid = sid.dup.upcase
533
534
# these can be validated using powershell where ?? is the code
535
# (ConvertFrom-SddlString -Sddl "O:??").RawDescriptor.Owner
536
case sid
537
when 'AA' # SDDL_ACCESS_CONTROL_ASSISTANCE_OPS
538
sid = Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_SID_ACCESS_CONTROL_ASSISTANCE_OPS
539
when 'AC' # SDDL_ALL_APP_PACKAGES
540
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_ALL_APP_PACKAGES
541
when 'AN' # SDDL_ANONYMOUS
542
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_ANONYMOUS_LOGON_SID
543
when 'AO' # SDDL_ACCOUNT_OPERATORS
544
sid = Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_SID_ACCOUNT_OPS
545
when 'AP' # SDDL_PROTECTED_USERS
546
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_PROTECTED_USERS}"
547
when 'AU' # SDDL_AUTHENTICATED_USERS
548
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_AUTHENTICATED_USER_SID
549
when 'BA' # SDDL_BUILTIN_ADMINISTRATORS
550
sid = Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_SID_ADMINS
551
when 'BG' # SDDL_BUILTIN_GUESTS
552
sid = Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_SID_GUESTS
553
when 'BO' # SDDL_BACKUP_OPERATORS
554
sid = Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_SID_BACKUP_OPS
555
when 'BU' # SDDL_BUILTIN_USERS
556
sid = Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_SID_USERS
557
when 'CA' # SDDL_CERT_SERV_ADMINISTRATORS
558
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_CERT_ADMINS}"
559
when 'CD' # SDDL_CERTSVC_DCOM_ACCESS
560
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP}"
561
when 'CG' # SDDL_CREATOR_GROUP
562
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_CREATOR_GROUP_SID
563
when 'CN' # SDDL_CLONEABLE_CONTROLLERS
564
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_CLONEABLE_CONTROLLERS}"
565
when 'CO' # SDDL_CREATOR_OWNER
566
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_CREATOR_OWNER_SID
567
when 'CY' # SDDL_CRYPTO_OPERATORS
568
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_CRYPTO_OPERATORS}"
569
when 'DA' # SDDL_DOMAIN_ADMINISTRATORS
570
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_ADMINS}"
571
when 'DC' # SDDL_DOMAIN_COMPUTERS
572
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_COMPUTERS}"
573
when 'DD' # SDDL_DOMAIN_DOMAIN_CONTROLLERS
574
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_CONTROLLERS}"
575
when 'DG' # SDDL_DOMAIN_GUESTS
576
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_GUESTS}"
577
when 'DU' # SDDL_DOMAIN_USERS
578
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_USERS}"
579
when 'EA' # SDDL_ENTERPRISE_ADMINS
580
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_ENTERPRISE_ADMINS}"
581
when 'ED' # SDDL_ENTERPRISE_DOMAIN_CONTROLLERS
582
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_ENTERPRISE_CONTROLLERS_SID}"
583
when 'EK' # SDDL_ENTERPRISE_KEY_ADMINS
584
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_ENTERPRISE_KEY_ADMINS}"
585
when 'ER' # SDDL_EVENT_LOG_READERS
586
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP}"
587
when 'ES' # SDDL_RDS_ENDPOINT_SERVERS
588
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_RDS_ENDPOINT_SERVERS}"
589
when 'HA' # SDDL_HYPER_V_ADMINS
590
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_HYPER_V_ADMINS}"
591
when 'HI' # SDDL_ML_HIGH
592
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_MANDATORY_HIGH_RID}"
593
when 'IS' # SDDL_IIS_USERS
594
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_IUSERS}"
595
when 'IU' # SDDL_INTERACTIVE
596
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_INTERACTIVE_SID
597
when 'KA' # SDDL_KEY_ADMINS
598
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_KEY_ADMINS}"
599
when 'LA' # SDDL_LOCAL_ADMIN
600
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_USER_RID_ADMIN}"
601
when 'LG' # SDDL_LOCAL_GUEST
602
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_USER_RID_GUEST}"
603
when 'LS' # SDDL_LOCAL_SERVICE
604
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_LOCAL_SERVICE_SID
605
when 'LU' # SDDL_PERFLOG_USERS
606
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_LOGGING_USERS}"
607
when 'LW' # SDDL_ML_LOW
608
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_MANDATORY_LOW_RID}"
609
when 'ME' # SDDL_ML_MEDIUM
610
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_MANDATORY_MEDIUM_RID}"
611
when 'MP' # SDDL_ML_MEDIUM_PLUS
612
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_MANDATORY_MEDIUM_PLUS_RID}"
613
when 'MU' # SDDL_PERFMON_USERS
614
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_MONITORING_USERS}"
615
when 'NO' # SDDL_NETWORK_CONFIGURATION_OPS
616
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS}"
617
when 'NS' # SDDL_NETWORK_SERVICE
618
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_NETWORK_SERVICE_SID
619
when 'NU' # SDDL_NETWORK
620
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_NETWORK_SID
621
when 'OW' # SDDL_OWNER_RIGHTS
622
sid = "#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_CREATOR_SID_AUTHORITY}-4"
623
when 'PA' # SDDL_GROUP_POLICY_ADMINS
624
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_POLICY_ADMINS}"
625
when 'PO' # SDDL_PRINTER_OPERATORS
626
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_PRINT_OPS}"
627
when 'PS' # SDDL_PERSONAL_SELF
628
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_PRINCIPAL_SELF_SID
629
when 'PU' # SDDL_POWER_USERS
630
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_POWER_USERS}"
631
when 'RA' # SDDL_RDS_REMOTE_ACCESS_SERVERS
632
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_RDS_REMOTE_ACCESS_SERVERS}"
633
when 'RC' # SDDL_RESTRICTED_CODE
634
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_RESTRICTED_CODE_SID
635
when 'RD' # SDDL_REMOTE_DESKTOP
636
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS}"
637
when 'RE' # SDDL_REPLICATOR
638
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_REPLICATOR}"
639
when 'RM' # SDDL_RMS__SERVICE_OPERATORS
640
sid = "#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_BUILTIN_DOMAIN_SID}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_REMOTE_MANAGEMENT_USERS}"
641
when 'RO' # SDDL_ENTERPRISE_RO_DCs
642
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS}"
643
when 'RS' # SDDL_RAS_SERVERS
644
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_RAS_SERVERS}"
645
when 'RU' # SDDL_ALIAS_PREW2KCOMPACC
646
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_PREW2KCOMPACCESS}"
647
when 'SA' # SDDL_SCHEMA_ADMINISTRATORS
648
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_GROUP_RID_SCHEMA_ADMINS}"
649
when 'SI' # SDDL_ML_SYSTEM
650
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_MANDATORY_SYSTEM_SID
651
when 'SO' # SDDL_SERVER_OPERATORS
652
sid = "#{domain_sid}-#{Rex::Proto::Secauthz::WellKnownSids::DOMAIN_ALIAS_RID_SYSTEM_OPS}"
653
when 'SS' # SDDL_SERVICE_ASSERTED
654
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_AUTHENTICATION_SERVICE_ASSERTED_SID
655
when 'SU' # SDDL_SERVICE
656
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_SERVICE_SID
657
when 'SY' # SDDL_LOCAL_SYSTEM
658
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_LOCAL_SYSTEM_SID
659
when 'UD' # SDDL_USER_MODE_DRIVERS
660
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_USERMODEDRIVERHOST_ID_BASE_SID
661
when 'WD' # SDDL_EVERYONE
662
sid = "#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_WORLD_SID_AUTHORITY}-#{Rex::Proto::Secauthz::WellKnownSids::SECURITY_WORLD_RID}"
663
when 'WR' # SDDL_WRITE_RESTRICTED_CODE
664
sid = Rex::Proto::Secauthz::WellKnownSids::SECURITY_WRITE_RESTRICTED_CODE_SID
665
when /^S(-\d+)+/
666
else
667
raise RuntimeError, 'SDDL parse error on invalid SID string: ' + sid
668
end
669
670
671
MsDtypSid.new(sid)
672
end
673
end
674
675
def initialize_shared_instance
676
# define accessor methods for the custom fields to expose the same API as BinData
677
define_field_accessors_for2(:owner_sid)
678
define_field_accessors_for2(:group_sid)
679
define_field_accessors_for2(:sacl)
680
define_field_accessors_for2(:dacl)
681
super
682
end
683
684
def initialize_instance
685
value = super
686
@owner_sid = get_parameter(:owner_sid)
687
@group_sid = get_parameter(:group_sid)
688
@sacl = get_parameter(:sacl)
689
@dacl = get_parameter(:dacl)
690
value
691
end
692
693
def do_read(val)
694
value = super
695
if offset_owner != 0
696
@owner_sid = MsDtypSid.read(buffer[offset_owner - buffer.rel_offset..])
697
end
698
if offset_group != 0
699
@group_sid = MsDtypSid.read(buffer[offset_group - buffer.rel_offset..])
700
end
701
if offset_sacl != 0
702
@sacl = MsDtypAcl.read(buffer[offset_sacl - buffer.rel_offset..])
703
end
704
if offset_dacl != 0
705
@dacl = MsDtypAcl.read(buffer[offset_dacl - buffer.rel_offset..])
706
end
707
value
708
end
709
710
def snapshot
711
snap = super
712
snap[:owner_sid] ||= owner_sid&.snapshot
713
snap[:group_sid] ||= group_sid&.snapshot
714
snap[:sacl] ||= sacl&.snapshot
715
snap[:dacl] ||= dacl&.snapshot
716
snap
717
end
718
719
attr_accessor :owner_sid, :group_sid, :sacl, :dacl
720
721
private
722
723
def build_buffer
724
buf = ''
725
buf << owner_sid.to_binary_s if owner_sid
726
buf << group_sid.to_binary_s if group_sid
727
buf << sacl.to_binary_s if sacl
728
buf << dacl.to_binary_s if dacl
729
buf
730
end
731
732
def define_field_accessors_for2(name)
733
define_singleton_method("#{name}?") do
734
!send(name).nil?
735
end
736
end
737
738
def offset_for(field)
739
return 0 unless instance_variable_get("@#{field}")
740
741
offset = buffer.rel_offset
742
%i[ owner_sid group_sid sacl dacl ].each do |cursor|
743
break if cursor == field
744
745
cursor = instance_variable_get("@#{cursor}")
746
offset += cursor.num_bytes if cursor
747
end
748
749
offset
750
end
751
end
752
753
# [2.3.7 LUID](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/48cbee2a-0790-45f2-8269-931d7083b2c3)
754
class MsDtypLuid < BinData::Record
755
endian :little
756
757
uint32 :low_part
758
int32 :high_part
759
760
def to_s
761
"0x#{high_part.to_i.to_s(16)}#{low_part.to_i.to_s(16).rjust(8, '0')}"
762
end
763
end
764
765
# [2.3.5 LARGE_INTEGER](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/e904b1ba-f774-4203-ba1b-66485165ab1a)
766
class MsDtypLargeInteger < BinData::Record
767
endian :big_and_little
768
769
uint32 :low_part
770
int32 :high_part
771
772
def to_datetime
773
RubySMB::Field::FileTime.new(to_i).to_datetime
774
end
775
776
def to_i
777
(high_part.to_i << 32) | low_part.to_i
778
end
779
end
780
end
781
782