@@ -49,24 +49,23 @@ class BESException < RuntimeError; end
49
49
50
50
# Requirements a browser module can define in either BrowserRequirements or in targets
51
51
REQUIREMENT_KEY_SET = Set . new ( [
52
- :source , # Either 'script' or 'headers'
53
- :ua_name , # Example: MSIE
54
- :ua_ver , # Example: 8.0, 9.0
55
- :os_name , # Example: Windows 7, Linux
56
- :os_device , # Example: iPad, iPhone, etc
57
- :os_vendor , # Example: Microsoft, Ubuntu, Apple, etc
58
- :os_sp , # Example: SP2
59
- :language , # Example: en-us
60
- :arch , # Example: x86
61
- :proxy , # 'true' or 'false'
62
- :silverlight , # 'true' or 'false'
63
- :office , # Example: "2007", "2010"
64
- :java , # Example: 1.6, 1.6.0.0
65
- :clsid , # ActiveX clsid. Also requires the :method key
66
- :method , # ActiveX method. Also requires the :clsid key
67
- :mshtml_build , # mshtml build. Example: "65535"
68
- :flash , # Example: "12.0" (chrome/ff) or "12.0.0.77" (IE)
69
- :vuln_test # Example: "if(window.MyComponentIsInstalled)return true;"
52
+ :source , # Return either 'script' or 'headers'
53
+ :ua_name , # Example: Returns 'MSIE'
54
+ :ua_ver , # Example: Returns '8.0', '9.0'
55
+ :os_name , # Example: Returns 'Windows 7', 'Linux'
56
+ :os_device , # Example: Returns 'iPad', 'iPhone', etc
57
+ :os_vendor , # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc
58
+ :os_sp , # Example: Returns 'SP2'
59
+ :language , # Example: Returns 'en-us'
60
+ :arch , # Example: Returns 'x86'
61
+ :proxy , # Returns 'true' or 'false'
62
+ :silverlight , # Returns 'true' or 'false'
63
+ :office , # Example: Returns "2007", "2010"
64
+ :java , # Example: Return '1.6', or maybe '1.6.0.0' (depends)
65
+ :mshtml_build , # mshtml build. Example: Returns "65535"
66
+ :flash , # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE)
67
+ :vuln_test # Example: "if(window.MyComponentIsInstalled)return true;",
68
+ :activex # Example: [{:clsid=>'String', :method=>'String'}]
70
69
] )
71
70
72
71
def initialize ( info = { } )
@@ -105,68 +104,61 @@ def setup
105
104
super
106
105
end
107
106
108
- #
107
+
109
108
# Returns the custom 404 URL set by the user
110
109
#
111
110
# @return [String]
112
- #
113
111
def get_custom_404_url
114
112
datastore [ 'Custom404' ] . to_s
115
113
end
116
114
117
- #
115
+
118
116
# Allows a block of code to access BES resources in a thread-safe fashion
119
117
#
120
118
# @param block [Proc] Block of code to sync
121
- #
122
119
def sync ( &block )
123
120
( @mutex ||= Mutex . new ) . synchronize ( &block )
124
121
end
125
122
126
- #
123
+
127
124
# Returns the resource (URI) to the module to allow access to on_request_exploit
128
125
#
129
126
# @return [String] URI to the exploit page
130
- #
131
127
def get_module_resource
132
128
"#{ get_resource . to_s . chomp ( "/" ) } /#{ @exploit_receiver_page } /"
133
129
end
134
130
135
- #
131
+
136
132
# Returns the absolute URL to the module's resource that points to on_request_exploit
137
133
#
138
134
# @return [String] absolute URI to the exploit page
139
- #
140
135
def get_module_uri
141
136
"#{ get_uri . chomp ( "/" ) } /#{ @exploit_receiver_page } "
142
137
end
143
138
144
- #
139
+
145
140
# Returns the current target
146
- #
147
141
def get_target
148
142
@target
149
143
end
150
144
151
- #
145
+
152
146
# Returns a hash of recognizable requirements
153
147
#
154
148
# @param reqs [Hash] A hash that contains data for the requirements
155
149
# @return [Hash] A hash of requirements
156
- #
157
150
def extract_requirements ( reqs )
158
151
tmp = reqs . select { |k , v | REQUIREMENT_KEY_SET . include? ( k . to_sym ) }
159
152
# Make sure keys are always symbols
160
153
Hash [ tmp . map { |( k , v ) | [ k . to_sym , v ] } ]
161
154
end
162
155
163
- #
156
+
164
157
# Sets the target automatically based on what requirements are met.
165
158
# If there's a possible matching target, it will also merge the requirements.
166
159
# You can use the get_target() method to retrieve the most current target.
167
160
#
168
161
# @param profile [Hash] The profile to check
169
- #
170
162
def try_set_target ( profile )
171
163
match_counts = [ ]
172
164
target_requirements = { }
@@ -195,30 +187,32 @@ def try_set_target(profile)
195
187
end
196
188
end
197
189
190
+ # Returns true if a bad ActiveX is found, otherwise false
198
191
#
192
+ # @param expected_ax [Array] ActiveX requirements set by the module
193
+ def has_bad_activex? ( user_ax )
194
+ user_ax . each do |a |
195
+ found = a [ :found ]
196
+ return true unless found
197
+ end
198
+
199
+ false
200
+ end
201
+
199
202
# Returns an array of items that do not meet the requirements
200
203
#
201
204
# @param profile [Hash] The profile to check
202
205
# @return [Array] An array of requirements not met
203
- #
204
206
def get_bad_requirements ( profile )
205
207
bad_reqs = [ ]
206
208
207
- # At this point the check is already done.
208
- # If :activex is true, that means the clsid + method had a match,
209
- # if not, then false.
210
- if @requirements [ :clsid ] and @requirements [ :method ]
211
- @requirements [ :activex ] = 'true' # Script passes boolean as string
212
- end
213
-
214
209
@requirements . each do |k , v |
215
- # Special keys to ignore because the script registers this as [:activex] = true or false
216
- next if k == :clsid or k == :method
217
-
218
210
expected = k != :vuln_test ? v : 'true'
219
211
vprint_debug ( "Comparing requirement: #{ k } =#{ expected } vs #{ k } =#{ profile [ k . to_sym ] } " )
220
212
221
- if k == :vuln_test
213
+ if k == :activex
214
+ bad_reqs << k unless has_bad_activex? ( v )
215
+ elsif k == :vuln_test
222
216
bad_reqs << k unless profile [ k . to_sym ] . to_s == 'true'
223
217
elsif v . is_a? Regexp
224
218
bad_reqs << k if profile [ k . to_sym ] !~ v
@@ -232,7 +226,6 @@ def get_bad_requirements(profile)
232
226
bad_reqs
233
227
end
234
228
235
- #
236
229
# Returns the target profile based on the tag. Each profile has the following structure:
237
230
# 'cookie_name' =>
238
231
# {
@@ -253,53 +246,49 @@ def get_bad_requirements(profile)
253
246
#
254
247
# If the source is 'script', the profile might have even more information about plugins:
255
248
# 'office' : The version of Microsoft Office (IE only)
256
- # 'activex' : Whether a specific method is available from an ActiveX control (IE only)
249
+ # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only)
257
250
# 'java' : The Java version
258
251
# 'mshtml_build' : The MSHTML build version
259
252
# 'flash' : The Flash version
260
253
# 'silverlight' : The Silverlight version
261
254
#
262
255
# @param tag [String] Either a cookie or IP + User-Agent
263
256
# @return [Hash] The profile found. If not found, returns nil
264
- #
265
257
def get_profile ( tag )
266
258
sync do
267
259
return @target_profiles [ tag ]
268
260
end
269
261
end
270
262
271
- #
263
+
272
264
# Updates information for a specific profile
273
265
#
274
266
# @param target_profile [Hash] The profile to update
275
267
# @param key [Symbol] The symbol to use for the hash
276
268
# @param value [String] The value to assign
277
- #
278
269
def update_profile ( target_profile , key , value )
279
270
sync do
280
271
target_profile [ key ] = value
281
272
end
282
273
end
283
274
284
- #
275
+
285
276
# Initializes a profile, if it did not previously exist
286
277
#
287
278
# @param tag [String] A unique string as a way to ID the profile
288
- #
289
279
def init_profile ( tag )
290
280
sync do
291
281
@target_profiles [ tag ] ||= { }
292
282
end
293
283
end
294
284
295
- #
285
+
296
286
# Retrieves a tag.
297
287
# First it obtains the tag from the browser's "Cookie" header.
298
288
# If the header is empty (possible if the browser has cookies disabled),
299
289
# then it will return a tag based on IP + the user-agent.
300
290
#
301
291
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
302
- #
303
292
def retrieve_tag ( cli , request )
304
293
cookie = CGI ::Cookie . parse ( request . headers [ 'Cookie' ] . to_s )
305
294
tag = cookie . has_key? ( cookie_name ) && cookie [ cookie_name ] . first
@@ -317,13 +306,12 @@ def retrieve_tag(cli, request)
317
306
tag
318
307
end
319
308
320
- #
309
+
321
310
# Registers target information to @target_profiles
322
311
#
323
312
# @param source [Symbol] Either :script, or :headers
324
313
# @param cli [Socket] Socket for the browser
325
314
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
326
- #
327
315
def process_browser_info ( source , cli , request )
328
316
tag = retrieve_tag ( cli , request )
329
317
init_profile ( tag )
@@ -361,23 +349,21 @@ def process_browser_info(source, cli, request)
361
349
} )
362
350
end
363
351
364
- #
352
+
365
353
# Checks if the target is running a proxy
366
354
#
367
355
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
368
356
# @return [Boolean] True if found, otherwise false
369
- #
370
357
def has_proxy? ( request )
371
358
proxy_header_set = PROXY_REQUEST_HEADER_SET & request . headers . keys
372
359
!proxy_header_set . empty?
373
360
end
374
361
375
- #
362
+
376
363
# Returns the code for client-side detection
377
364
#
378
365
# @param user_agent [String] The user-agent of the browser
379
366
# @return [String] Returns the HTML for detection
380
- #
381
367
def get_detection_html ( user_agent )
382
368
ua_info = fingerprint_user_agent ( user_agent )
383
369
os = ua_info [ :os_name ]
@@ -418,11 +404,20 @@ def get_detection_html(user_agent)
418
404
d['office'] = ie_addons_detect.getMsOfficeVersion();
419
405
d['mshtml_build'] = ScriptEngineBuildVersion().toString();
420
406
<%
421
- clsid = @requirements[:clsid]
422
- method = @requirements[:method]
423
- if clsid and method
407
+ activex = @requirements[:activex]
408
+ if activex
424
409
%>
425
- d['activex'] = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
410
+ d['activex'] = '';
411
+ <%
412
+ activex.each do \| a\|
413
+ clsid = a[:clsid]
414
+ method = a[:method]
415
+ %>
416
+ var ax = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
417
+ if (ax == false) {
418
+ d['activex'] += "<%=clsid%>=<%=method%>;";
419
+ }
420
+ <% end %>
426
421
<% end %>
427
422
<% end %>
428
423
@@ -462,12 +457,11 @@ def cookie_header(tag)
462
457
cookie
463
458
end
464
459
465
- #
460
+
466
461
# Handles exploit stages.
467
462
#
468
463
# @param cli [Socket] Socket for the browser
469
464
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
470
- #
471
465
def on_request_uri ( cli , request )
472
466
case request . uri
473
467
when '/' , get_resource . chomp ( "/" )
@@ -548,26 +542,24 @@ def on_request_uri(cli, request)
548
542
end
549
543
end
550
544
551
- #
545
+
552
546
# Overriding method. The module should override this.
553
547
#
554
548
# @param cli [Socket] Socket for the browser
555
549
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
556
550
# @param browser_info [Hash] The target profile
557
- #
558
551
def on_request_exploit ( cli , request , browser_info )
559
552
raise NoMethodError , "Module must define its own on_request_exploit method"
560
553
end
561
554
562
- #
555
+
563
556
# Converts an ERB-based exploit template into HTML, and sends to client
564
557
#
565
558
# @param cli [Socket] Socket for the browser
566
559
# @param template [String] The ERB template. If you want to pass the binding object,
567
560
# then this is handled as an Array, with the first element
568
561
# being the HTML, and the second element is the binding object.
569
562
# @param headers [Hash] The custom HTTP headers to include in the response
570
- #
571
563
def send_exploit_html ( cli , template , headers = { } )
572
564
html = ''
573
565
if template . class == Array
@@ -578,13 +570,12 @@ def send_exploit_html(cli, template, headers={})
578
570
send_response ( cli , html , headers )
579
571
end
580
572
581
- #
573
+
582
574
# Generates a target-specific payload, should be called by the module
583
575
#
584
576
# @param cli [Socket] Socket for the browser
585
577
# @param browser_info [Hash] The target profile
586
578
# @return [String] The payload
587
- #
588
579
def get_payload ( cli , browser_info )
589
580
arch = browser_info [ :arch ]
590
581
platform = browser_info [ :os_name ]
@@ -618,9 +609,8 @@ def js_vuln_test
618
609
619
610
private
620
611
621
- #
612
+
622
613
# Sends a 404 respons. If a custom 404 is configured, then it will redirect to that instead.
623
- #
624
614
def send_not_found ( cli )
625
615
custom_404_url = get_custom_404_url
626
616
if custom_404_url . blank?
0 commit comments