Repository navigation
Expand file tree
/
Copy pathconfig.ml
More file actions
299 lines (258 loc) · 12.2 KB
/
Copy pathconfig.ml
File metadata and controls
299 lines (258 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
set AllowChangePassword = true
set DefaultSetFullControl = true
set AllAddKeyCredentialLink = false
set AllowAddComputer = true
set DoGPOAddLocalAdmin = true
set DoGPOLogonScript = false
set DoGPOImmediateTask = false
set DoGPODisableDefender = false
# Group RIDs
set BACKUP_OPERATORS = 551
set ACCOUNT_OPERATORS = 548
set KEY_ADMINS = 526
set ENTERPRISE_KEY_ADMINS = 527
set REMOTE_DESKTOP_USERS = 555
set DOMAIN_CONTROLLERS = "DOMAIN CONTROLLERS@" # TODO: check languages
#
# Convention:
# ::NAME(target) = this is an action (generated code is in templates/)
# __NAME(target) = this is a temporary state
# NAME(target) = this is a bloodhound right
#
# The parent is the object we own (we have his password or a ticket) and the one
# which has the right on the target.
# Full documentation here: doc/config.md
#
# Private actions
# The password is in hexa, it's easier to manage the computer with
# its AES key
::_Secretsdump(computer) -> ::_TransformPasswordToAES
::_TransformPasswordToAES(any) -> apply_with_aes
# GMSA first because below AllExtendedRights is checked before GenericWrite
# -> prefer AddGMSAReader instead of ForceChangePassword
GenericAll(user) -> ::AddGMSAReader if target.is_gmsa
GenericWrite(user) -> ::AddGMSAReader if target.is_gmsa
ReadGMSAPassword(user) -> ::ReadGMSAPassword
::AddGMSAReader(user) -> ::ReadGMSAPassword
::ReadGMSAPassword(user) -> apply_with_aes
# FullControl, let Griffon choose the best scenario
# any != many
GenericAll(any) -> AllExtendedRights
GenericAll(any) -> GenericWrite
# Unconstrained delegation
# TRUSTED_FOR_DELEGATION: userAccountControl & 0x80000
# It could be any computer instead of the dc, if we coerce the dc we are admin!
# TODO: delegation parameter on a user (actually only on computer)
AllowedToDelegate(many) -> ::AllowedToDelegateToAny require_targets ta_dc
::AllowedToDelegateToAny(dc) -> apply_with_ticket \
if not parent.sensitive and not parent.protected \
elsewarn "PARENT -> AllowedToDelegateToAny(TARGET): PARENT is sensitive or protected"
# 'Account Operators'
GenericAll(many) -> GenericAll \
require_targets ta_users_and_groups_without_admincount \
if ACCOUNT_OPERATORS in parent.groups
# 'Key Admins' or 'Enterprise Key Admins'
AddKeyCredentialLink(many) -> ::AddKeyCredentialLink \
require_targets ta_users_without_admincount \
if AllAddKeyCredentialLink and \
(KEY_ADMINS in parent.groups or ENTERPRISE_KEY_ADMINS in parent.groups) \
elsewarn "Set the flag AllAddKeyCredentialLink in config.ml to execute the scenario AddKeyCredentialLink(many)"
# PASSWD_NOTREQD: userAccountControl & 0x20
::BlankPassword(user) -> apply_with_blank_passwd
::BlankPassword(computer) -> apply_with_blank_passwd
::CanRDP_RegSave(dc) -> apply_with_aes
::CanRDP_RegSave(computer) -> apply_with_aes
# User
WriteUserAccountControl(user) -> ::EnableUser if target.disabled
::EnableUser(user) -> restart
ForceChangePassword(user) -> ::ForceChangePassword if not target.disabled
AddKeyCredentialLink(user) -> ::AddKeyCredentialLink
WriteUserAccountControl(user) -> ::EnableNP
WriteSPN(user) -> ::WriteSPN
SetLogonScript(user) -> ::SetLogonScript
AllExtendedRights(user) -> ForceChangePassword
GenericWrite(user) -> AddKeyCredentialLink
GenericWrite(user) -> WriteUserAccountControl
GenericWrite(user) -> WriteSPN
GenericWrite(user) -> SetLogonScript
WriteDacl(user) -> ::DaclResetPassword
WriteDacl(user) -> ::DaclKeyCredentialLink
WriteDacl(user) -> ::DaclUserAccountControl
WriteDacl(user) -> ::DaclServicePrincipalName
WriteDacl(user) -> ::DaclInitialProgram
::DaclResetPassword(user) -> ForceChangePassword
::DaclKeyCredentialLink(user) -> AddKeyCredentialLink
::DaclUserAccountControl(user) -> WriteUserAccountControl
::DaclServicePrincipalName(user) -> WriteSPN
::DaclInitialProgram(user) -> SetLogonScript
::ForceChangePassword(user) -> apply_with_forced_passwd \
if AllowChangePassword and not target.disabled
::AddKeyCredentialLink(user) -> apply_with_ticket if not target.disabled
::Kerberoasting(user) -> apply_with_cracked_passwd \
require_for_auth any_owned \
if target.has_spn and not target.protected and not target.disabled \
elsewarn "warning: TARGET seems to be kerberoastable, but I need an owned user to request the TGS"
::SetLogonScript(user) -> stop if not target.disabled
::WriteSPN(user) -> ::Kerberoasting if not target.disabled
SessionForUser(user) -> ::LSASS_dumper
# computer -> LSASS_dumper(user)
# it means we own the computer and we know that the user has a session on it
::LSASS_dumper(user) -> apply_with_nthash
# DONT_REQ_PREAUTH: userAccountControl & 0x400000
::EnableNP(user) -> ::ASREPRoasting
::ASREPRoasting(user) -> apply_with_cracked_passwd if target.np
# Computer
AdminTo(computer) -> ::_Secretsdump
SeBackupPrivilege(computer) -> ::RegBackup
ReadLAPSPassword(computer) -> ::ReadLAPSPassword
::ReadLAPSPassword(computer) -> ::_TransformPasswordToAES
::RegBackup(computer) -> ::_TransformPasswordToAES
# RBCD
# In the computer ldap object:
# - msDS-AllowedToActOnBehalfOfOtherIdentity: contains the allowed accounts
# - msDS-AllowedToDelegateTo: contains the list of allowed SPNs
# Griffon inverts the relation and sets the AllowedToAct right on the account
# (the parent which executes this statement)
AllowedToAct(computer) -> ::AllowedToAct if parent.has_spn
AllowedToAct(computer) -> ::U2U
AddAllowedToAct(computer) -> ::RBCD
::RBCD(computer) -> ::AllowedToAct require unprotected_owned_with_spn
::RBCD(computer) -> ::AllowedToAct require add_computer if AllowAddComputer
::RBCD(computer) -> ::U2U require owned_user_without_spn
::U2U(computer) -> ::AllowedToAct if parent.is_user
::AllowedToAct(computer) -> ::_Secretsdump \
if not parent.sensitive and not parent.protected \
elsewarn "PARENT -> AllowedToAct(TARGET): PARENT is sensitive or protected"
# Constrained delegations (with/without protocol transition)
# msDS-AllowedToDelegateTo contains a list of SPNs
AllowedToDelegate(computer) -> __AllowedToDelegate_ok \
if not parent.sensitive and not parent.protected \
elsewarn "PARENT -> AllowedToDelegate(TARGET): PARENT is sensitive or protected"
# Constrained delegations with protocol transition
# TRUSTED_TO_AUTH_FOR_DELEGATION: userAccountControl & 0x1000000
__AllowedToDelegate_ok(computer) => ::SPNJacking if parent.trustedtoauth
__AllowedToDelegate_ok(computer) -> ::AllowedToDelegate if parent.trustedtoauth
# else
# Constrained delegations without protocol transition (kerberos only)
#
# Mimic Kerberos protocol transition using reflective RBCD
# https://medium.com/tenable-techblog/how-to-mimic-kerberos-protocol-transition-using-reflective-rbcd-a4984bb7c4cb
#
# It also works if trustedtoauth is True but we check trustedtoauth to avoid
# duplicated paths
#
# Here we use the 'require_once' and not the 'require' like the normal RBCD
# For the RBCD: the 'require' is used to authenticate after
# Here we just need an object to create the SelfRBCD, then we don't use it anymore
#
# TODO: U2U?
__AllowedToDelegate_ok(computer) -> ::SelfRBCD if not parent.trustedtoauth
::SelfRBCD(computer) -> __NextSelfRBCD require_once unprotected_owned_with_spn_not_eq_parent
::SelfRBCD(computer) -> __NextSelfRBCD require_once add_computer if AllowAddComputer
__NextSelfRBCD(computer) => ::SPNJacking
__NextSelfRBCD(computer) -> ::AllowedToDelegate
::AllowedToDelegate(computer) -> ::_Secretsdump
::SPNJacking(computer) -> ::AllowedToDelegate require_targets ta_spn_jacking_requirements
WriteAccountRestrictions(computer) -> AddAllowedToAct
AddKeyCredentialLink(computer) -> ::AddKeyCredentialLink
GenericWrite(computer) -> AddAllowedToAct
# correct but it duplicates the path AddAllowedToAct
# GenericWrite(computer) -> WriteAccountRestrictions
GenericWrite(computer) -> AddKeyCredentialLink
WriteDacl(computer) -> ::DaclAccountRestrictions
WriteDacl(computer) -> ::DaclKeyCredentialLink
# DaclAllowedToAct is an other alternative to DaclAccountRestrictions
# WriteDacl(computer) -> ::DaclAllowedToAct
# ::DaclAllowedToAct(computer) -> AddAllowedToAct
::DaclAccountRestrictions(computer) -> AddAllowedToAct
::DaclKeyCredentialLink(computer) -> AddKeyCredentialLink
::AddKeyCredentialLink(computer) -> apply_with_ticket
# Group
AddMember(group) -> ::AddMember
AddSelf(group) -> ::AddMember
GenericWrite(group) -> AddMember
GenericWrite(group) -> AddSelf
WriteDacl(group) -> ::DaclMemberShips
WriteDacl(group) -> ::DaclSelf
::DaclMemberShips(group) -> AddMember
::DaclSelf(group) -> AddSelf
::AddMember(group) -> apply_group
# Domain
AllExtendedRights(domain) -> GetChanges_GetChangesAll
AllExtendedRights(domain) -> GetChanges_GetChangesInFilteredSet
GetChanges_GetChangesAll(domain) -> ::DCSync
GetChanges_GetChangesInFilteredSet(domain) -> ::DCSync if parent.is_dc
::DCSync(domain) -> stop
# from GPOs
AdminTo(domain) -> ::DCSync # local Administrator
# If the GPO is applied on the entire domaine
# also ok on all computers but we are admin here!
SeBackupPrivilege(domain) -> ::RegBackup \
require_targets ta_dc \
if BACKUP_OPERATORS in parent.groups or \
BACKUP_OPERATORS in parent.restricted_groups_rids
# FIXME: a simple user cannot RDP on a on the DC even if he is
# in the RDP Users group?
SeBackupPrivilege(domain) -> ::CanRDP_RegSave \
require_targets ta_all_computers_in_domain \
if BACKUP_OPERATORS not in parent.groups and \
BACKUP_OPERATORS not in parent.restricted_groups_rids and \
REMOTE_DESKTOP_USERS in parent.restricted_groups_rids
# DC
GenericWrite(dc) -> AddKeyCredentialLink
AddKeyCredentialLink(dc) -> ::AddKeyCredentialLink
::AddKeyCredentialLink(dc) -> apply_with_ticket
::RegBackup(dc) -> apply_with_aes
# GPO
GenericWrite(gpo) -> ::GPOLogonScript if DoGPOLogonScript
GenericWrite(gpo) -> ::GPOImmediateTask if DoGPOImmediateTask
GenericWrite(gpo) -> ::GPODisableDefender if DoGPODisableDefender
GenericWrite(gpo) -> ::GPOAddLocalAdmin if DoGPOAddLocalAdmin
WriteDacl(gpo) -> ::DaclFullControl
# Execute a command
::GPOImmediateTask(gpo) => stop require_targets ta_all_computers_in_ou
::GPOImmediateTask(gpo) => stop require_targets ta_all_users_in_ou
# Execute a script (Startup / Logon)
::GPOLogonScript(gpo) => stop require_targets ta_all_computers_in_ou
::GPOLogonScript(gpo) => stop require_targets ta_all_users_in_ou
# Disable windows defender + disable firewall + enable RDP
::GPODisableDefender(gpo) => stop require_targets ta_all_computers_in_ou
# Stop forking for the last predicate
# Set the parent in the local Administrators group (via the RestrictedGroups)
::GPOAddLocalAdmin(gpo) -> ::_Secretsdump require_targets ta_all_computers_in_ou
# OU
# Unimplemented
# https://www.synacktiv.com/publications/ounedpy-exploiting-hidden-organizational-units-acl-attack-vectors-in-active-directory
# https://markgamache.blogspot.com/2020/07/exploiting-ad-gplink-for-good-or-evil.html
GenericWrite(ou) -> WriteGPLink
WriteGPLink(ou) -> ::WriteGPLink
::WriteGPLink(ou) -> stop
# If the user is a direct member of Backup Operators or
# a GPO is applied on the 'Domain Controllers' container
SeBackupPrivilege(ou) -> __IsInBackupGroup \
if BACKUP_OPERATORS in parent.groups or \
BACKUP_OPERATORS in parent.restricted_groups_rids
SeBackupPrivilege(ou) -> __NotInBackupGroup \
if BACKUP_OPERATORS not in parent.groups and \
BACKUP_OPERATORS not in parent.restricted_groups_rids
__IsInBackupGroup(ou) -> ::RegBackup \
require_targets ta_one_dc_in_ou \
if DOMAIN_CONTROLLERS in target.name
AdminTo(ou) -> AdminTo require_targets ta_all_computers_in_ou
__IsInBackupGroup(ou) -> ::RegBackup \
require_targets ta_all_computers_in_ou \
if DOMAIN_CONTROLLERS not in target.name
# The user has only the SeBackup privilege and is NOT in the Backup
# Operators group, then we need to RDP to enable the backup privilege
# It seems that the access is denied on the DC (if the GPO is applied on
# the 'Domain Controllers' container
__NotInBackupGroup(ou) -> ::CanRDP_RegSave \
require_targets ta_all_computers_in_ou \
if REMOTE_DESKTOP_USERS in parent.restricted_groups_rids
# Last chance
__WriteDacl(any) -> ::DaclFullControl if DefaultSetFullControl
__WriteDacl(any) -> WriteDacl if not DefaultSetFullControl
::DaclFullControl(any) -> GenericAll
Owns(any) -> __WriteDacl
::WriteOwner(any) -> Owns
WriteOwner(any) -> ::WriteOwner