Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

6# of the Apache Software License. See the accompanying LICENSE file 

7# for more information. 

8# 

9# Description: 

10# Structures and types used in LDAP 

11# Contains the Structures for the NT Security Descriptor (non-RPC format) and 

12# all ACL related structures 

13# 

14# Author: 

15# Dirk-jan Mollema (@_dirkjan) / Fox-IT (https://www.fox-it.com) 

16# 

17from struct import unpack, pack 

18from impacket.structure import Structure 

19 

20# Global constant if the library should recalculate ACE sizes in objects that are decoded/re-encoded. 

21# This defaults to True, but this causes the ACLs to not match on a binary level 

22# since Active Directory for some reason sometimes adds null bytes to the end of ACEs. 

23# This is valid according to the spec (see 2.4.4), but since impacket encodes them more efficiently 

24# this should be turned off if running unit tests. 

25RECALC_ACE_SIZE = True 

26 

27# LDAP SID structure - based on SAMR_RPC_SID, except the SubAuthority is LE here 

28class LDAP_SID_IDENTIFIER_AUTHORITY(Structure): 

29 structure = ( 

30 ('Value','6s'), 

31 ) 

32 

33class LDAP_SID(Structure): 

34 structure = ( 

35 ('Revision','<B'), 

36 ('SubAuthorityCount','<B'), 

37 ('IdentifierAuthority',':',LDAP_SID_IDENTIFIER_AUTHORITY), 

38 ('SubLen','_-SubAuthority','self["SubAuthorityCount"]*4'), 

39 ('SubAuthority',':'), 

40 ) 

41 

42 def formatCanonical(self): 

43 ans = 'S-%d-%d' % (self['Revision'], ord(self['IdentifierAuthority']['Value'][5:6])) 

44 for i in range(self['SubAuthorityCount']): 

45 ans += '-%d' % ( unpack('<L',self['SubAuthority'][i*4:i*4+4])[0]) 

46 return ans 

47 

48 def fromCanonical(self, canonical): 

49 items = canonical.split('-') 

50 self['Revision'] = int(items[1]) 

51 self['IdentifierAuthority'] = LDAP_SID_IDENTIFIER_AUTHORITY() 

52 self['IdentifierAuthority']['Value'] = b'\x00\x00\x00\x00\x00' + pack('B',int(items[2])) 

53 self['SubAuthorityCount'] = len(items) - 3 

54 self['SubAuthority'] = b'' 

55 for i in range(self['SubAuthorityCount']): 

56 self['SubAuthority'] += pack('<L', int(items[i+3])) 

57 

58""" 

59Self-relative security descriptor as described in 2.4.6 

60https://msdn.microsoft.com/en-us/library/cc230366.aspx 

61""" 

62class SR_SECURITY_DESCRIPTOR(Structure): 

63 structure = ( 

64 ('Revision','c'), 

65 ('Sbz1','c'), 

66 ('Control','<H'), 

67 ('OffsetOwner','<L'), 

68 ('OffsetGroup','<L'), 

69 ('OffsetSacl','<L'), 

70 ('OffsetDacl','<L'), 

71 ('Sacl',':'), 

72 ('Dacl',':'), 

73 ('OwnerSid',':'), 

74 ('GroupSid',':'), 

75 ) 

76 

77 def fromString(self, data): 

78 Structure.fromString(self, data) 

79 # All these fields are optional, if the offset is 0 they are empty 

80 # there are also flags indicating if they are present 

81 # TODO: parse those if it adds value 

82 if self['OffsetOwner'] != 0: 

83 self['OwnerSid'] = LDAP_SID(data=data[self['OffsetOwner']:]) 

84 else: 

85 self['OwnerSid'] = b'' 

86 

87 if self['OffsetGroup'] != 0: 

88 self['GroupSid'] = LDAP_SID(data=data[self['OffsetGroup']:]) 

89 else: 

90 self['GroupSid'] = b'' 

91 

92 if self['OffsetSacl'] != 0: 

93 self['Sacl'] = ACL(data=data[self['OffsetSacl']:]) 

94 else: 

95 self['Sacl'] = b'' 

96 

97 if self['OffsetDacl'] != 0: 

98 self['Dacl'] = ACL(data=data[self['OffsetDacl']:]) 

99 else: 

100 self['Sacl'] = b'' 

101 

102 def getData(self): 

103 headerlen = 20 

104 # Reconstruct the security descriptor 

105 # flags are currently not set automatically 

106 # TODO: do this? 

107 datalen = 0 

108 if self['Sacl'] != b'': 

109 self['OffsetSacl'] = headerlen + datalen 

110 datalen += len(self['Sacl'].getData()) 

111 else: 

112 self['OffsetSacl'] = 0 

113 

114 if self['Dacl'] != b'': 

115 self['OffsetDacl'] = headerlen + datalen 

116 datalen += len(self['Dacl'].getData()) 

117 else: 

118 self['OffsetDacl'] = 0 

119 

120 if self['OwnerSid'] != b'': 

121 self['OffsetOwner'] = headerlen + datalen 

122 datalen += len(self['OwnerSid'].getData()) 

123 else: 

124 self['OffsetOwner'] = 0 

125 

126 if self['GroupSid'] != b'': 

127 self['OffsetGroup'] = headerlen + datalen 

128 datalen += len(self['GroupSid'].getData()) 

129 else: 

130 self['OffsetGroup'] = 0 

131 return Structure.getData(self) 

132 

133""" 

134ACE as described in 2.4.4 

135https://msdn.microsoft.com/en-us/library/cc230295.aspx 

136""" 

137class ACE(Structure): 

138 # Flag constants 

139 CONTAINER_INHERIT_ACE = 0x02 

140 FAILED_ACCESS_ACE_FLAG = 0x80 

141 INHERIT_ONLY_ACE = 0x08 

142 INHERITED_ACE = 0x10 

143 NO_PROPAGATE_INHERIT_ACE = 0x04 

144 OBJECT_INHERIT_ACE = 0x01 

145 SUCCESSFUL_ACCESS_ACE_FLAG = 0x40 

146 

147 structure = ( 

148 # 

149 # ACE_HEADER as described in 2.4.4.1 

150 # https://msdn.microsoft.com/en-us/library/cc230296.aspx 

151 # 

152 ('AceType','B'), 

153 ('AceFlags','B'), 

154 ('AceSize','<H'), 

155 # Virtual field to calculate data length from AceSize 

156 ('AceLen', '_-Ace', 'self["AceSize"]-4'), 

157 # 

158 # ACE body, is parsed depending on the type 

159 # 

160 ('Ace',':') 

161 ) 

162 

163 def fromString(self, data): 

164 # This will parse the header 

165 Structure.fromString(self, data) 

166 # Now we parse the ACE body according to its type 

167 self['TypeName'] = ACE_TYPE_MAP[self['AceType']].__name__ 

168 self['Ace'] = ACE_TYPE_MAP[self['AceType']](data=self['Ace']) 

169 

170 def getData(self): 

171 if RECALC_ACE_SIZE or 'AceSize' not in self.fields: 

172 self['AceSize'] = len(self['Ace'].getData())+4 # Header size (4 bytes) is included 

173 if self['AceSize'] % 4 != 0: 

174 # Make sure the alignment is correct 

175 self['AceSize'] += self['AceSize'] % 4 

176 data = Structure.getData(self) 

177 # For some reason ACEs are sometimes longer than they need to be 

178 # we fill this space up with null bytes to make sure the object 

179 # we create is identical to the original object 

180 if len(data) < self['AceSize']: 

181 data += '\x00' * (self['AceSize'] - len(data)) 

182 return data 

183 

184 def hasFlag(self, flag): 

185 return self['AceFlags'] & flag == flag 

186 

187""" 

188ACCESS_MASK as described in 2.4.3 

189https://msdn.microsoft.com/en-us/library/cc230294.aspx 

190""" 

191class ACCESS_MASK(Structure): 

192 # Flag constants 

193 GENERIC_READ = 0x80000000 

194 GENERIC_WRITE = 0x40000000 

195 GENERIC_EXECUTE = 0x20000000 

196 GENERIC_ALL = 0x10000000 

197 MAXIMUM_ALLOWED = 0x02000000 

198 ACCESS_SYSTEM_SECURITY = 0x01000000 

199 SYNCHRONIZE = 0x00100000 

200 WRITE_OWNER = 0x00080000 

201 WRITE_DACL = 0x00040000 

202 READ_CONTROL = 0x00020000 

203 DELETE = 0x00010000 

204 

205 structure = ( 

206 ('Mask', '<L'), 

207 ) 

208 

209 def hasPriv(self, priv): 

210 return self['Mask'] & priv == priv 

211 

212 def setPriv(self, priv): 

213 self['Mask'] |= priv 

214 

215 def removePriv(self, priv): 

216 self['Mask'] ^= priv 

217 

218""" 

219ACCESS_ALLOWED_ACE as described in 2.4.4.2 

220https://msdn.microsoft.com/en-us/library/cc230286.aspx 

221""" 

222class ACCESS_ALLOWED_ACE(Structure): 

223 ACE_TYPE = 0x00 

224 structure = ( 

225 ('Mask', ':', ACCESS_MASK), 

226 ('Sid', ':', LDAP_SID) 

227 ) 

228 

229""" 

230ACCESS_ALLOWED_OBJECT_ACE as described in 2.4.4.3 

231https://msdn.microsoft.com/en-us/library/cc230289.aspx 

232""" 

233class ACCESS_ALLOWED_OBJECT_ACE(Structure): 

234 ACE_TYPE = 0x05 

235 

236 # Flag contstants 

237 ACE_OBJECT_TYPE_PRESENT = 0x01 

238 ACE_INHERITED_OBJECT_TYPE_PRESENT = 0x02 

239 

240 # ACE type specific mask constants 

241 # Note that while not documented, these also seem valid 

242 # for ACCESS_ALLOWED_ACE types 

243 ADS_RIGHT_DS_CONTROL_ACCESS = 0x00000100 

244 ADS_RIGHT_DS_CREATE_CHILD = 0x00000001 

245 ADS_RIGHT_DS_DELETE_CHILD = 0x00000002 

246 ADS_RIGHT_DS_READ_PROP = 0x00000010 

247 ADS_RIGHT_DS_WRITE_PROP = 0x00000020 

248 ADS_RIGHT_DS_SELF = 0x00000008 

249 

250 

251 structure = ( 

252 ('Mask', ':', ACCESS_MASK), 

253 ('Flags', '<L'), 

254 # Optional field 

255 ('ObjectTypeLen','_-ObjectType','self.checkObjectType(self["Flags"])'), 

256 ('ObjectType', ':=""'), 

257 # Optional field 

258 ('InheritedObjectTypeLen','_-InheritedObjectType','self.checkInheritedObjectType(self["Flags"])'), 

259 ('InheritedObjectType', ':=""'), 

260 ('Sid', ':', LDAP_SID) 

261 ) 

262 

263 @staticmethod 

264 def checkInheritedObjectType(flags): 

265 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_INHERITED_OBJECT_TYPE_PRESENT: 

266 return 16 

267 return 0 

268 

269 @staticmethod 

270 def checkObjectType(flags): 

271 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT: 

272 return 16 

273 return 0 

274 

275 def getData(self): 

276 # Set the correct flags 

277 if self['ObjectType'] != b'': 

278 self['Flags'] |= self.ACE_OBJECT_TYPE_PRESENT 

279 if self['InheritedObjectType'] != b'': 

280 self['Flags'] |= self.ACE_INHERITED_OBJECT_TYPE_PRESENT 

281 return Structure.getData(self) 

282 

283 def hasFlag(self, flag): 

284 return self['Flags'] & flag == flag 

285 

286""" 

287ACCESS_DENIED_ACE as described in 2.4.4.4 

288https://msdn.microsoft.com/en-us/library/cc230291.aspx 

289Structure is identical to ACCESS_ALLOWED_ACE 

290""" 

291class ACCESS_DENIED_ACE(ACCESS_ALLOWED_ACE): 

292 ACE_TYPE = 0x01 

293 

294""" 

295ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.5 

296https://msdn.microsoft.com/en-us/library/gg750297.aspx 

297Structure is identical to ACCESS_ALLOWED_OBJECT_ACE 

298""" 

299class ACCESS_DENIED_OBJECT_ACE(ACCESS_ALLOWED_OBJECT_ACE): 

300 ACE_TYPE = 0x06 

301 

302""" 

303ACCESS_ALLOWED_CALLBACK_ACE as described in 2.4.4.6 

304https://msdn.microsoft.com/en-us/library/cc230287.aspx 

305""" 

306class ACCESS_ALLOWED_CALLBACK_ACE(Structure): 

307 ACE_TYPE = 0x09 

308 structure = ( 

309 ('Mask', ':', ACCESS_MASK), 

310 ('Sid', ':', LDAP_SID), 

311 ('ApplicationData', ':') 

312 ) 

313 

314""" 

315ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.7 

316https://msdn.microsoft.com/en-us/library/cc230292.aspx 

317Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE 

318""" 

319class ACCESS_DENIED_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE): 

320 ACE_TYPE = 0x0A 

321 

322""" 

323ACCESS_ALLOWED_CALLBACK_OBJECT_ACE as described in 2.4.4.8 

324https://msdn.microsoft.com/en-us/library/cc230288.aspx 

325""" 

326class ACCESS_ALLOWED_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_OBJECT_ACE): 

327 ACE_TYPE = 0x0B 

328 structure = ( 

329 ('Mask', ':', ACCESS_MASK), 

330 ('Flags', '<L'), 

331 # Optional field 

332 ('ObjectTypeLen','_-ObjectType','self.checkObjectType(self["Flags"])'), 

333 ('ObjectType', ':=""'), 

334 # Optional field 

335 ('InheritedObjectTypeLen','_-InheritedObjectType','self.checkInheritedObjectType(self["Flags"])'), 

336 ('InheritedObjectType', ':=""'), 

337 ('Sid', ':', LDAP_SID), 

338 ('ApplicationData', ':') 

339 ) 

340 

341""" 

342ACCESS_DENIED_CALLBACK_OBJECT_ACE as described in 2.4.4.7 

343https://msdn.microsoft.com/en-us/library/cc230292.aspx 

344Structure is identical to ACCESS_ALLOWED_OBJECT_OBJECT_ACE 

345""" 

346class ACCESS_DENIED_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE): 

347 ACE_TYPE = 0x0C 

348 

349""" 

350SYSTEM_AUDIT_ACE as described in 2.4.4.10 

351https://msdn.microsoft.com/en-us/library/cc230376.aspx 

352Structure is identical to ACCESS_ALLOWED_ACE 

353""" 

354class SYSTEM_AUDIT_ACE(ACCESS_ALLOWED_ACE): 

355 ACE_TYPE = 0x02 

356 

357 

358""" 

359SYSTEM_AUDIT_OBJECT_ACE as described in 2.4.4.11 

360https://msdn.microsoft.com/en-us/library/gg750298.aspx 

361Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE 

362""" 

363class SYSTEM_AUDIT_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE): 

364 ACE_TYPE = 0x07 

365 

366 

367""" 

368SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.12 

369https://msdn.microsoft.com/en-us/library/cc230377.aspx 

370Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE 

371""" 

372class SYSTEM_AUDIT_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE): 

373 ACE_TYPE = 0x0D 

374 

375""" 

376SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.13 

377https://msdn.microsoft.com/en-us/library/cc230379.aspx 

378Structure is identical to ACCESS_ALLOWED_ACE, but with custom masks and meanings. 

379Lets keep it separate for now 

380""" 

381class SYSTEM_MANDATORY_LABEL_ACE(Structure): 

382 ACE_TYPE = 0x11 

383 structure = ( 

384 ('Mask', ':', ACCESS_MASK), 

385 ('Sid', ':', LDAP_SID) 

386 ) 

387 

388""" 

389SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.14 

390https://msdn.microsoft.com/en-us/library/cc230378.aspx 

391Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE 

392""" 

393class SYSTEM_AUDIT_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE): 

394 ACE_TYPE = 0x0F 

395 

396""" 

397SYSTEM_RESOURCE_ATTRIBUTE_ACE as described in 2.4.4.15 

398https://msdn.microsoft.com/en-us/library/hh877837.aspx 

399Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE 

400The application data however is encoded in CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 

401format as described in section 2.4.10.1 

402Todo: implement this substructure if needed 

403""" 

404class SYSTEM_RESOURCE_ATTRIBUTE_ACE(ACCESS_ALLOWED_CALLBACK_ACE): 

405 ACE_TYPE = 0x12 

406 

407 

408""" 

409SYSTEM_SCOPED_POLICY_ID_ACE as described in 2.4.4.16 

410https://msdn.microsoft.com/en-us/library/hh877846.aspx 

411Structure is identical to ACCESS_ALLOWED_ACE 

412The Sid data MUST match a CAPID of a CentralAccessPolicy 

413contained in the CentralAccessPoliciesList 

414Todo: implement this substructure if needed 

415Also the ACCESS_MASK must always be 0 

416""" 

417class SYSTEM_SCOPED_POLICY_ID_ACE(ACCESS_ALLOWED_ACE): 

418 ACE_TYPE = 0x13 

419 

420# All the ACE types in a list 

421ACE_TYPES = [ 

422 ACCESS_ALLOWED_ACE, 

423 ACCESS_ALLOWED_OBJECT_ACE, 

424 ACCESS_DENIED_ACE, 

425 ACCESS_DENIED_OBJECT_ACE, 

426 ACCESS_ALLOWED_CALLBACK_ACE, 

427 ACCESS_DENIED_CALLBACK_ACE, 

428 ACCESS_ALLOWED_CALLBACK_OBJECT_ACE, 

429 ACCESS_DENIED_CALLBACK_OBJECT_ACE, 

430 SYSTEM_AUDIT_ACE, 

431 SYSTEM_AUDIT_OBJECT_ACE, 

432 SYSTEM_AUDIT_CALLBACK_ACE, 

433 SYSTEM_MANDATORY_LABEL_ACE, 

434 SYSTEM_AUDIT_CALLBACK_OBJECT_ACE, 

435 SYSTEM_RESOURCE_ATTRIBUTE_ACE, 

436 SYSTEM_SCOPED_POLICY_ID_ACE 

437] 

438 

439# A dict of all the ACE types indexed by their type number 

440ACE_TYPE_MAP = {ace.ACE_TYPE: ace for ace in ACE_TYPES} 

441 

442""" 

443ACL as described in 2.4.5 

444https://msdn.microsoft.com/en-us/library/cc230297.aspx 

445""" 

446class ACL(Structure): 

447 structure = ( 

448 ('AclRevision', 'B'), 

449 ('Sbz1', 'B'), 

450 ('AclSize', '<H'), 

451 ('AceCount', '<H'), 

452 ('Sbz2', '<H'), 

453 # Virtual field to calculate data length from AclSize 

454 ('DataLen', '_-Data', 'self["AclSize"]-8'), 

455 ('Data', ':'), 

456 ) 

457 

458 def fromString(self, data): 

459 self.aces = [] 

460 Structure.fromString(self, data) 

461 for i in range(self['AceCount']): 

462 # If we don't have any data left, return 

463 if len(self['Data']) == 0: 

464 raise Exception("ACL header indicated there are more ACLs to unpack, but there is no more data") 

465 ace = ACE(data=self['Data']) 

466 self.aces.append(ace) 

467 self['Data'] = self['Data'][ace['AceSize']:] 

468 self['Data'] = self.aces 

469 

470 def getData(self): 

471 self['AceCount'] = len(self.aces) 

472 # We modify the data field to be able to use the 

473 # parent class parsing 

474 self['Data'] = b''.join([ace.getData() for ace in self.aces]) 

475 self['AclSize'] = len(self['Data'])+8 # Header size (8 bytes) is included 

476 data = Structure.getData(self) 

477 # Put the ACEs back in data 

478 self['Data'] = self.aces 

479 return data 

480 

481""" 

482objectClass mapping to GUID for some common classes (index is the ldapDisplayName). 

483Reference: 

484 https://msdn.microsoft.com/en-us/library/ms680938(v=vs.85).aspx 

485Can also be queried from the Schema 

486""" 

487OBJECTTYPE_GUID_MAP = { 

488 b'group': 'bf967a9c-0de6-11d0-a285-00aa003049e2', 

489 b'domain': '19195a5a-6da0-11d0-afd3-00c04fd930c9', 

490 b'organizationalUnit': 'bf967aa5-0de6-11d0-a285-00aa003049e2', 

491 b'user': 'bf967aba-0de6-11d0-a285-00aa003049e2', 

492 b'groupPolicyContainer': 'f30e3bc2-9ff0-11d1-b603-0000f80367c1' 

493}