Skip to content

Commit 1565203

Browse files
committed
Making some changes to how BES handles ActiveX
1 parent 1a2a78b commit 1565203

File tree

2 files changed

+69
-75
lines changed

2 files changed

+69
-75
lines changed

lib/msf/core/exploit/remote/browser_exploit_server.rb

Lines changed: 63 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,23 @@ class BESException < RuntimeError; end
4949

5050
# Requirements a browser module can define in either BrowserRequirements or in targets
5151
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'}]
7069
])
7170

7271
def initialize(info={})
@@ -105,68 +104,61 @@ def setup
105104
super
106105
end
107106

108-
#
107+
109108
# Returns the custom 404 URL set by the user
110109
#
111110
# @return [String]
112-
#
113111
def get_custom_404_url
114112
datastore['Custom404'].to_s
115113
end
116114

117-
#
115+
118116
# Allows a block of code to access BES resources in a thread-safe fashion
119117
#
120118
# @param block [Proc] Block of code to sync
121-
#
122119
def sync(&block)
123120
(@mutex ||= Mutex.new).synchronize(&block)
124121
end
125122

126-
#
123+
127124
# Returns the resource (URI) to the module to allow access to on_request_exploit
128125
#
129126
# @return [String] URI to the exploit page
130-
#
131127
def get_module_resource
132128
"#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/"
133129
end
134130

135-
#
131+
136132
# Returns the absolute URL to the module's resource that points to on_request_exploit
137133
#
138134
# @return [String] absolute URI to the exploit page
139-
#
140135
def get_module_uri
141136
"#{get_uri.chomp("/")}/#{@exploit_receiver_page}"
142137
end
143138

144-
#
139+
145140
# Returns the current target
146-
#
147141
def get_target
148142
@target
149143
end
150144

151-
#
145+
152146
# Returns a hash of recognizable requirements
153147
#
154148
# @param reqs [Hash] A hash that contains data for the requirements
155149
# @return [Hash] A hash of requirements
156-
#
157150
def extract_requirements(reqs)
158151
tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)}
159152
# Make sure keys are always symbols
160153
Hash[tmp.map{|(k,v)| [k.to_sym,v]}]
161154
end
162155

163-
#
156+
164157
# Sets the target automatically based on what requirements are met.
165158
# If there's a possible matching target, it will also merge the requirements.
166159
# You can use the get_target() method to retrieve the most current target.
167160
#
168161
# @param profile [Hash] The profile to check
169-
#
170162
def try_set_target(profile)
171163
match_counts = []
172164
target_requirements = {}
@@ -195,30 +187,32 @@ def try_set_target(profile)
195187
end
196188
end
197189

190+
# Returns true if a bad ActiveX is found, otherwise false
198191
#
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+
199202
# Returns an array of items that do not meet the requirements
200203
#
201204
# @param profile [Hash] The profile to check
202205
# @return [Array] An array of requirements not met
203-
#
204206
def get_bad_requirements(profile)
205207
bad_reqs = []
206208

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-
214209
@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-
218210
expected = k != :vuln_test ? v : 'true'
219211
vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}")
220212

221-
if k == :vuln_test
213+
if k == :activex
214+
bad_reqs << k unless has_bad_activex?(v)
215+
elsif k == :vuln_test
222216
bad_reqs << k unless profile[k.to_sym].to_s == 'true'
223217
elsif v.is_a? Regexp
224218
bad_reqs << k if profile[k.to_sym] !~ v
@@ -232,7 +226,6 @@ def get_bad_requirements(profile)
232226
bad_reqs
233227
end
234228

235-
#
236229
# Returns the target profile based on the tag. Each profile has the following structure:
237230
# 'cookie_name' =>
238231
# {
@@ -253,53 +246,49 @@ def get_bad_requirements(profile)
253246
#
254247
# If the source is 'script', the profile might have even more information about plugins:
255248
# '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)
257250
# 'java' : The Java version
258251
# 'mshtml_build' : The MSHTML build version
259252
# 'flash' : The Flash version
260253
# 'silverlight' : The Silverlight version
261254
#
262255
# @param tag [String] Either a cookie or IP + User-Agent
263256
# @return [Hash] The profile found. If not found, returns nil
264-
#
265257
def get_profile(tag)
266258
sync do
267259
return @target_profiles[tag]
268260
end
269261
end
270262

271-
#
263+
272264
# Updates information for a specific profile
273265
#
274266
# @param target_profile [Hash] The profile to update
275267
# @param key [Symbol] The symbol to use for the hash
276268
# @param value [String] The value to assign
277-
#
278269
def update_profile(target_profile, key, value)
279270
sync do
280271
target_profile[key] = value
281272
end
282273
end
283274

284-
#
275+
285276
# Initializes a profile, if it did not previously exist
286277
#
287278
# @param tag [String] A unique string as a way to ID the profile
288-
#
289279
def init_profile(tag)
290280
sync do
291281
@target_profiles[tag] ||= {}
292282
end
293283
end
294284

295-
#
285+
296286
# Retrieves a tag.
297287
# First it obtains the tag from the browser's "Cookie" header.
298288
# If the header is empty (possible if the browser has cookies disabled),
299289
# then it will return a tag based on IP + the user-agent.
300290
#
301291
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
302-
#
303292
def retrieve_tag(cli, request)
304293
cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s)
305294
tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first
@@ -317,13 +306,12 @@ def retrieve_tag(cli, request)
317306
tag
318307
end
319308

320-
#
309+
321310
# Registers target information to @target_profiles
322311
#
323312
# @param source [Symbol] Either :script, or :headers
324313
# @param cli [Socket] Socket for the browser
325314
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
326-
#
327315
def process_browser_info(source, cli, request)
328316
tag = retrieve_tag(cli, request)
329317
init_profile(tag)
@@ -361,23 +349,21 @@ def process_browser_info(source, cli, request)
361349
})
362350
end
363351

364-
#
352+
365353
# Checks if the target is running a proxy
366354
#
367355
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
368356
# @return [Boolean] True if found, otherwise false
369-
#
370357
def has_proxy?(request)
371358
proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys
372359
!proxy_header_set.empty?
373360
end
374361

375-
#
362+
376363
# Returns the code for client-side detection
377364
#
378365
# @param user_agent [String] The user-agent of the browser
379366
# @return [String] Returns the HTML for detection
380-
#
381367
def get_detection_html(user_agent)
382368
ua_info = fingerprint_user_agent(user_agent)
383369
os = ua_info[:os_name]
@@ -418,11 +404,20 @@ def get_detection_html(user_agent)
418404
d['office'] = ie_addons_detect.getMsOfficeVersion();
419405
d['mshtml_build'] = ScriptEngineBuildVersion().toString();
420406
<%
421-
clsid = @requirements[:clsid]
422-
method = @requirements[:method]
423-
if clsid and method
407+
activex = @requirements[:activex]
408+
if activex
424409
%>
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 %>
426421
<% end %>
427422
<% end %>
428423
@@ -462,12 +457,11 @@ def cookie_header(tag)
462457
cookie
463458
end
464459

465-
#
460+
466461
# Handles exploit stages.
467462
#
468463
# @param cli [Socket] Socket for the browser
469464
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
470-
#
471465
def on_request_uri(cli, request)
472466
case request.uri
473467
when '/', get_resource.chomp("/")
@@ -548,26 +542,24 @@ def on_request_uri(cli, request)
548542
end
549543
end
550544

551-
#
545+
552546
# Overriding method. The module should override this.
553547
#
554548
# @param cli [Socket] Socket for the browser
555549
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
556550
# @param browser_info [Hash] The target profile
557-
#
558551
def on_request_exploit(cli, request, browser_info)
559552
raise NoMethodError, "Module must define its own on_request_exploit method"
560553
end
561554

562-
#
555+
563556
# Converts an ERB-based exploit template into HTML, and sends to client
564557
#
565558
# @param cli [Socket] Socket for the browser
566559
# @param template [String] The ERB template. If you want to pass the binding object,
567560
# then this is handled as an Array, with the first element
568561
# being the HTML, and the second element is the binding object.
569562
# @param headers [Hash] The custom HTTP headers to include in the response
570-
#
571563
def send_exploit_html(cli, template, headers={})
572564
html = ''
573565
if template.class == Array
@@ -578,13 +570,12 @@ def send_exploit_html(cli, template, headers={})
578570
send_response(cli, html, headers)
579571
end
580572

581-
#
573+
582574
# Generates a target-specific payload, should be called by the module
583575
#
584576
# @param cli [Socket] Socket for the browser
585577
# @param browser_info [Hash] The target profile
586578
# @return [String] The payload
587-
#
588579
def get_payload(cli, browser_info)
589580
arch = browser_info[:arch]
590581
platform = browser_info[:os_name]
@@ -618,9 +609,8 @@ def js_vuln_test
618609

619610
private
620611

621-
#
612+
622613
# Sends a 404 respons. If a custom 404 is configured, then it will redirect to that instead.
623-
#
624614
def send_not_found(cli)
625615
custom_404_url = get_custom_404_url
626616
if custom_404_url.blank?

modules/exploits/windows/browser/adobe_flash_avm2.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,12 @@ module has been tested successfully with Adobe Flash Player 11.7.700.202 on Wind
6666
'BrowserRequirements' =>
6767
{
6868
:source => /script|headers/i,
69-
:clsid => "{D27CDB6E-AE6D-11cf-96B8-444553540000}",
70-
:method => "LoadMovie",
69+
:activex => [
70+
{
71+
:clsid => '{D27CDB6E-AE6D-11cf-96B8-444553540000}',
72+
:method => 'LoadMovie'
73+
}
74+
],
7175
:os_name => OperatingSystems::Match::WINDOWS,
7276
:ua_name => Msf::HttpClients::IE,
7377
:flash => lambda { |ver| ver =~ /^11\./ }

0 commit comments

Comments
 (0)