@@ -49,24 +49,30 @@ 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 is a special case.
69
+ # When you set this requirement in your module, this is how it should be:
70
+ # [{:clsid=>'String', :method=>'String'}]
71
+ # Where each Hash is a test case
72
+ # But when BES receives this information, the JavaScript will return this format:
73
+ # "{CLSID}=>Method=>Boolean;"
74
+ # Also see: #has_bad_activex?
75
+ :activex
70
76
] )
71
77
72
78
def initialize ( info = { } )
@@ -105,68 +111,61 @@ def setup
105
111
super
106
112
end
107
113
108
- #
114
+
109
115
# Returns the custom 404 URL set by the user
110
116
#
111
117
# @return [String]
112
- #
113
118
def get_custom_404_url
114
119
datastore [ 'Custom404' ] . to_s
115
120
end
116
121
117
- #
122
+
118
123
# Allows a block of code to access BES resources in a thread-safe fashion
119
124
#
120
125
# @param block [Proc] Block of code to sync
121
- #
122
126
def sync ( &block )
123
127
( @mutex ||= Mutex . new ) . synchronize ( &block )
124
128
end
125
129
126
- #
130
+
127
131
# Returns the resource (URI) to the module to allow access to on_request_exploit
128
132
#
129
133
# @return [String] URI to the exploit page
130
- #
131
134
def get_module_resource
132
135
"#{ get_resource . to_s . chomp ( "/" ) } /#{ @exploit_receiver_page } /"
133
136
end
134
137
135
- #
138
+
136
139
# Returns the absolute URL to the module's resource that points to on_request_exploit
137
140
#
138
141
# @return [String] absolute URI to the exploit page
139
- #
140
142
def get_module_uri
141
143
"#{ get_uri . chomp ( "/" ) } /#{ @exploit_receiver_page } "
142
144
end
143
145
144
- #
146
+
145
147
# Returns the current target
146
- #
147
148
def get_target
148
149
@target
149
150
end
150
151
151
- #
152
+
152
153
# Returns a hash of recognizable requirements
153
154
#
154
155
# @param reqs [Hash] A hash that contains data for the requirements
155
156
# @return [Hash] A hash of requirements
156
- #
157
157
def extract_requirements ( reqs )
158
158
tmp = reqs . select { |k , v | REQUIREMENT_KEY_SET . include? ( k . to_sym ) }
159
159
# Make sure keys are always symbols
160
160
Hash [ tmp . map { |( k , v ) | [ k . to_sym , v ] } ]
161
161
end
162
162
163
- #
163
+
164
164
# Sets the target automatically based on what requirements are met.
165
165
# If there's a possible matching target, it will also merge the requirements.
166
166
# You can use the get_target() method to retrieve the most current target.
167
167
#
168
168
# @param profile [Hash] The profile to check
169
- #
170
169
def try_set_target ( profile )
171
170
match_counts = [ ]
172
171
target_requirements = { }
@@ -195,30 +194,36 @@ def try_set_target(profile)
195
194
end
196
195
end
197
196
198
- #
197
+
198
+ # Returns true if there's a bad ActiveX, otherwise false.
199
+ # @param ax [String] The raw activex the JavaScript detection will return in this format:
200
+ # "{CLSID}=>Method=>Boolean;"
201
+ # @return [Boolean] True if there's a bad ActiveX, otherwise false
202
+ def has_bad_activex? ( ax )
203
+ ax . split ( ';' ) . each do |a |
204
+ bool = a . split ( '=>' ) [ 2 ]
205
+ if bool == 'false'
206
+ return true
207
+ end
208
+ end
209
+
210
+ false
211
+ end
212
+
199
213
# Returns an array of items that do not meet the requirements
200
214
#
201
215
# @param profile [Hash] The profile to check
202
216
# @return [Array] An array of requirements not met
203
- #
204
217
def get_bad_requirements ( profile )
205
218
bad_reqs = [ ]
206
219
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
220
@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
221
expected = k != :vuln_test ? v : 'true'
219
222
vprint_debug ( "Comparing requirement: #{ k } =#{ expected } vs #{ k } =#{ profile [ k . to_sym ] } " )
220
223
221
- if k == :vuln_test
224
+ if k == :activex
225
+ bad_reqs << k if has_bad_activex? ( profile [ k . to_sym ] )
226
+ elsif k == :vuln_test
222
227
bad_reqs << k unless profile [ k . to_sym ] . to_s == 'true'
223
228
elsif v . is_a? Regexp
224
229
bad_reqs << k if profile [ k . to_sym ] !~ v
@@ -232,7 +237,6 @@ def get_bad_requirements(profile)
232
237
bad_reqs
233
238
end
234
239
235
- #
236
240
# Returns the target profile based on the tag. Each profile has the following structure:
237
241
# 'cookie_name' =>
238
242
# {
@@ -253,53 +257,49 @@ def get_bad_requirements(profile)
253
257
#
254
258
# If the source is 'script', the profile might have even more information about plugins:
255
259
# 'office' : The version of Microsoft Office (IE only)
256
- # 'activex' : Whether a specific method is available from an ActiveX control (IE only)
260
+ # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only)
257
261
# 'java' : The Java version
258
262
# 'mshtml_build' : The MSHTML build version
259
263
# 'flash' : The Flash version
260
264
# 'silverlight' : The Silverlight version
261
265
#
262
266
# @param tag [String] Either a cookie or IP + User-Agent
263
267
# @return [Hash] The profile found. If not found, returns nil
264
- #
265
268
def get_profile ( tag )
266
269
sync do
267
270
return @target_profiles [ tag ]
268
271
end
269
272
end
270
273
271
- #
274
+
272
275
# Updates information for a specific profile
273
276
#
274
277
# @param target_profile [Hash] The profile to update
275
278
# @param key [Symbol] The symbol to use for the hash
276
279
# @param value [String] The value to assign
277
- #
278
280
def update_profile ( target_profile , key , value )
279
281
sync do
280
282
target_profile [ key ] = value
281
283
end
282
284
end
283
285
284
- #
286
+
285
287
# Initializes a profile, if it did not previously exist
286
288
#
287
289
# @param tag [String] A unique string as a way to ID the profile
288
- #
289
290
def init_profile ( tag )
290
291
sync do
291
292
@target_profiles [ tag ] ||= { }
292
293
end
293
294
end
294
295
295
- #
296
+
296
297
# Retrieves a tag.
297
298
# First it obtains the tag from the browser's "Cookie" header.
298
299
# If the header is empty (possible if the browser has cookies disabled),
299
300
# then it will return a tag based on IP + the user-agent.
300
301
#
301
302
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
302
- #
303
303
def retrieve_tag ( cli , request )
304
304
cookie = CGI ::Cookie . parse ( request . headers [ 'Cookie' ] . to_s )
305
305
tag = cookie . has_key? ( cookie_name ) && cookie [ cookie_name ] . first
@@ -317,13 +317,12 @@ def retrieve_tag(cli, request)
317
317
tag
318
318
end
319
319
320
- #
320
+
321
321
# Registers target information to @target_profiles
322
322
#
323
323
# @param source [Symbol] Either :script, or :headers
324
324
# @param cli [Socket] Socket for the browser
325
325
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
326
- #
327
326
def process_browser_info ( source , cli , request )
328
327
tag = retrieve_tag ( cli , request )
329
328
init_profile ( tag )
@@ -361,27 +360,28 @@ def process_browser_info(source, cli, request)
361
360
} )
362
361
end
363
362
364
- #
363
+
365
364
# Checks if the target is running a proxy
366
365
#
367
366
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
368
367
# @return [Boolean] True if found, otherwise false
369
- #
370
368
def has_proxy? ( request )
371
369
proxy_header_set = PROXY_REQUEST_HEADER_SET & request . headers . keys
372
370
!proxy_header_set . empty?
373
371
end
374
372
375
- #
373
+
376
374
# Returns the code for client-side detection
377
375
#
378
376
# @param user_agent [String] The user-agent of the browser
379
377
# @return [String] Returns the HTML for detection
380
- #
381
378
def get_detection_html ( user_agent )
379
+ print_debug ( user_agent )
382
380
ua_info = fingerprint_user_agent ( user_agent )
383
381
os = ua_info [ :os_name ]
384
382
client = ua_info [ :ua_name ]
383
+ print_debug ( os . inspect )
384
+ print_debug ( client . inspect )
385
385
386
386
code = ERB . new ( %Q|
387
387
<%= js_base64 %>
@@ -418,11 +418,20 @@ def get_detection_html(user_agent)
418
418
d['office'] = ie_addons_detect.getMsOfficeVersion();
419
419
d['mshtml_build'] = ScriptEngineBuildVersion().toString();
420
420
<%
421
- clsid = @requirements[:clsid]
422
- method = @requirements[:method]
423
- if clsid and method
421
+ activex = @requirements[:activex]
422
+ if activex
423
+ activex.each do \| a\|
424
+ clsid = a[:clsid]
425
+ method = a[:method]
424
426
%>
425
- d['activex'] = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
427
+ var ax = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
428
+ d['activex'] = "";
429
+ if (ax == true) {
430
+ d['activex'] += "<%=clsid%>=><%=method%>=>true;";
431
+ } else {
432
+ d['activex'] += "<%=clsid%>=><%=method%>=>false;";
433
+ }
434
+ <% end %>
426
435
<% end %>
427
436
<% end %>
428
437
@@ -438,7 +447,7 @@ def get_detection_html(user_agent)
438
447
439
448
%Q|
440
449
<script>
441
- #{ js }
450
+ #{ code }
442
451
</script>
443
452
<noscript>
444
453
<img style="visibility:hidden" src="#{ get_resource . chomp ( "/" ) } /#{ @noscript_receiver_page } /">
@@ -462,12 +471,11 @@ def cookie_header(tag)
462
471
cookie
463
472
end
464
473
465
- #
474
+
466
475
# Handles exploit stages.
467
476
#
468
477
# @param cli [Socket] Socket for the browser
469
478
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
470
- #
471
479
def on_request_uri ( cli , request )
472
480
case request . uri
473
481
when '/' , get_resource . chomp ( "/" )
@@ -548,26 +556,24 @@ def on_request_uri(cli, request)
548
556
end
549
557
end
550
558
551
- #
559
+
552
560
# Overriding method. The module should override this.
553
561
#
554
562
# @param cli [Socket] Socket for the browser
555
563
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
556
564
# @param browser_info [Hash] The target profile
557
- #
558
565
def on_request_exploit ( cli , request , browser_info )
559
566
raise NoMethodError , "Module must define its own on_request_exploit method"
560
567
end
561
568
562
- #
569
+
563
570
# Converts an ERB-based exploit template into HTML, and sends to client
564
571
#
565
572
# @param cli [Socket] Socket for the browser
566
573
# @param template [String] The ERB template. If you want to pass the binding object,
567
574
# then this is handled as an Array, with the first element
568
575
# being the HTML, and the second element is the binding object.
569
576
# @param headers [Hash] The custom HTTP headers to include in the response
570
- #
571
577
def send_exploit_html ( cli , template , headers = { } )
572
578
html = ''
573
579
if template . class == Array
@@ -578,13 +584,12 @@ def send_exploit_html(cli, template, headers={})
578
584
send_response ( cli , html , headers )
579
585
end
580
586
581
- #
587
+
582
588
# Generates a target-specific payload, should be called by the module
583
589
#
584
590
# @param cli [Socket] Socket for the browser
585
591
# @param browser_info [Hash] The target profile
586
592
# @return [String] The payload
587
- #
588
593
def get_payload ( cli , browser_info )
589
594
arch = browser_info [ :arch ]
590
595
platform = browser_info [ :os_name ]
@@ -618,9 +623,8 @@ def js_vuln_test
618
623
619
624
private
620
625
621
- #
626
+
622
627
# Sends a 404 respons. If a custom 404 is configured, then it will redirect to that instead.
623
- #
624
628
def send_not_found ( cli )
625
629
custom_404_url = get_custom_404_url
626
630
if custom_404_url . blank?
0 commit comments