How to write a browser exploit using BrowserExploitServer

sinn3r edited this page Mar 14, 2016 · 48 revisions

Metasploit Wiki Pages


Clone this wiki locally

The Metasploit Framework provides different mixins you can use to develop a browser exploit, mainly they are:

The Automatic Exploitation Procedure

The BrowserExploitServer mixin is the only mixin specially designed for browser exploitation. Before you use this mixin, you should understand what it does behind the scenes for you:

  1. It automatically collects the browser information, including things like: OS name, version, browser name, browser version, whether a proxy is used, Java plugin version, Microsoft Office version, etc, etc. If the browser doesn't have Javascript enabled, then it knows less about the target. All the info gathered will be stored in a profile managed by the mixin.
  2. The mixin will then tag the browser to track the session. It will also use the same tag to retrieve the profile when needed.
  3. Before the mixin decides if it should serve the exploit to the browser, it will check with the module for any exploitable requirements. If the requirements aren't met, it will send a 404 to the browser, and the operation bails.
  4. If the requirements are met, the mixin will pass the profile (information about the browser gathered during the detection stage) to the module, and let it take over the rest.

Hint: In the module, you can check the :source key in the profile to determine whether Javascript is enabled or not: If the :source is "script", it means Javascript is enabled. If it's "headers" (as in HTTP headers), then the browser has Javascript disabled.

Setting Exploitable Requirements

Being able to set browser requirements is an important feature of the mixin. It allows your attack to be smarter, more targeted, and prevents accidents. Here's a scenario: Say you have a vulnerability against Internet Explorer that only affects a specific range of MSHTML builds, you can set the :os_name, :ua_name, :ua_ver, and :mshtml_build to make sure it doesn't blindly exploit against anything else. The :mshtml_build requirement can be found in "Product version" under MSHTML's file properties.

Exploitable browser requirements are defined under "BrowserRequirements" in the module's metadata. Here's an example of defining a vulnerable target running some ActiveX control:

'BrowserRequirements' =>
{
    source: /script/i,
        activex: [
          {
            clsid: '{D27CDB6E-AE6D-11cf-96B8-444553540000}',
            method: 'LoadMovie'
          }
        ],
    os_name: /win/i
}

You can also define target-specific requirements. This is also how the mixin is able to automatically select a target, and you can get it with the "get_target" method. Here's an example of how to define target-specific requirements for IE8 on Win XP and IE 9 on Win 7:

'BrowserRequirements' =>
  {
    :source   => /script|headers/i,
    'ua_name' => HttpClients::IE,
  },
'Targets'             =>
  [
    [ 'Automatic', {} ],
    [
      'Windows XP with IE 8',
      {
        :os_name    => 'Windows XP',
        'ua_ver'    => '8.0',
        'Rop'       => true,
        'Offset'    => 0x100
      }
    ],
    [
      'Windows 7 with IE 9',
      {
        'os_name'   => 'Windows 7',
        'ua_ver'    => '9.0',
        'Rop'       => true,
        'Offset'    => 0x200
      }
    ]
  ]

You can use these for :os_name:

Constant Purpose
OperatingSystems::Match::WINDOWS Match all versions of Windows
OperatingSystems::Match::WINDOWS_95 Match Windows 95
OperatingSystems::Match::WINDOWS_98 Match Windows 98
OperatingSystems::Match::WINDOWS_ME Match Windows ME
OperatingSystems::Match::WINDOWS_NT3 Match Windows NT 3
OperatingSystems::Match::WINDOWS_NT4 Match Windows NT 4
OperatingSystems::Match::WINDOWS_2000 Match Windows 2000
OperatingSystems::Match::WINDOWS_XP Match Windows XP
OperatingSystems::Match::WINDOWS_2003 Match Windows Server 2003
OperatingSystems::Match::WINDOWS_VISTA Match Windows Vista
OperatingSystems::Match::WINDOWS_2008 Match Windows Server 2008
OperatingSystems::Match::WINDOWS_7 Match Windows 7
OperatingSystems::Match::WINDOWS_2012 Match Windows 2012
OperatingSystems::Match::WINDOWS_8 Match Windows 8
OperatingSystems::Match::WINDOWS_81 Match Windows 8.1
OperatingSystems::Match::LINUX Match a Linux distro
OperatingSystems::Match::MAC_OSX Match Mac OSX
OperatingSystems::Match::FREEBSD Match FreeBSD
OperatingSystems::Match::NETBSD Match NetBSD
OperatingSystems::Match::OPENBSD Match OpenBSD
OperatingSystems::Match::VMWARE Match VMWare
OperatingSystems::Match::ANDROID Match Android
OperatingSystems::Match::APPLE_IOS Match Apple IOS

You can use these for :ua_name:

Constant Value
HttpClients::IE "MSIE"
HttpClients::FF "Firefox"
HttpClients::SAFARI "Safari"
HttpClients::OPERA "Opera"
HttpClients::CHROME "Chrome"

More of these constants can be found here: https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/constants.rb

All currently supported requirements by the mixin can be found here (see REQUIREMENT_KEY_SET): https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/exploit/remote/browser_exploit_server.rb#L46

Set up a listener

After the detection stage and the requirement check, the mixin will trigger the "on_request_exploit" callback method, that's where you handle the HTTP request, craft the HTML, and send back the exploit response. Here's an example of how to set up "on_request_exploit":

#
# Listens for the HTTP request
# cli is the socket
# request is the Rex::Proto::Http::Request object
# target_info is a hash that contains all the browser info (aka the profile)
#
def on_request_exploit(cli, request, target_info)
    print_status("Here's what I know about the target: #{target_info.inspect}")
end

Crafting HTML with BrowserExploitServer

There are two coding styles the BrowserExploitServer mixin supports: The good old HTML, or ERB template. The first is pretty self-explanatory:

def on_request_exploit(cli, request, target_info)
    html = %Q|
    <html>
    Hello, world!
    </html>
    |
    send_exploit_html(cli, html)
end 

ERB is a new way to write Metasploit browser exploits. If you've written one or two web applications, this is no stranger to you. When you're using the BrowserExploitServer mixin to write an exploit, what really happens is you're writing a rails template. Here's an example of using of this feature:

def on_request_exploit(cli, request, target_info)
    html = %Q|
    <html>
    Do you feel lucky, punk?<br>
    <% if [true, false].sample %>
    Lucky!<br>
    <% else %>
    Bad luck, bro!<Br>
    <% end %>
    </html>
    |
    send_exploit_html(cli, html)
end

If you want to access local variables or arguments, make sure to pass the binding object to send_exploit_html:

def exploit_template1(target_info, txt)
    txt2 = "I can use local vars!"

    template = %Q|
    <% msg = "This page is generated by an exploit" %>
    <%=msg%><br>
    <%=txt%><br>
    <%=txt2%><br>
    <p></p>
    Data gathered from source: #{target_info[:source]}<br>
    OS name: #{target_info[:os_name]}<br>
    UA name: #{target_info[:ua_name]}<br>
    UA version: #{target_info[:ua_ver]}<br>
    Java version: #{target_info[:java]}<br>
    Office version: #{target_info[:office]}
    |

    return template, binding()
end

def on_request_exploit(cli, request, target_info)
    send_exploit_html(cli, exploit_template(target_info, txt))
end

The BrowserExploitServer mixin also offers plenty of other things useful while crafting the exploit. For example: it can generate a target-specific payload when you call the "get_payload" method. It also gives you access to the RopDb mixin, which contains a collection of ROPs to bypass DEP (Data Execution Prevention). Make sure to check out the API documentation for more information.

To get thing started, here's a code example you can use start developing your browser exploit:

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::BrowserExploitServer

  def initialize(info={})
    super(update_info(info,
      'Name'           => "BrowserExploitServer Example",
      'Description'    => %q{
        This is an example of building a browser exploit using the BrowserExploitServer mixin
      },
      'License'        => MSF_LICENSE,
      'Author'         => [ 'sinn3r' ],
      'References'     =>
        [
          [ 'URL', 'http://metasploit.com' ]
        ],
      'Platform'       => 'win',
      'BrowserRequirements' =>
        {
          :source => /script|headers/i,
        },
      'Targets'        =>
        [
          [ 'Automatic', {} ],
          [
            'Windows XP with IE 8',
            {
              'os_name'   => 'Windows XP',
              'ua_name'   => 'MSIE',
              'ua_ver'    => '8.0'
            }
          ],
          [
            'Windows 7 with IE 9',
            {
              'os_name'   => 'Windows 7',
              'ua_name'   => 'MSIE',
              'ua_ver'    => '9.0'
            }
          ]
        ],
      'Payload'        => { 'BadChars' => "\x00" },
      'DisclosureDate' => "Apr 1 2013",
      'DefaultTarget'  => 0))
  end

  def exploit_template(target_info)
    template = %Q|
    Data source: <%=target_info[:source]%><br>
    OS name: <%=target_info[:os_name]%><br>
    UA name: <%=target_info[:ua_name]%><br>
    UA version: <%=target_info[:ua_ver]%><br>
    Java version: <%=target_info[:java]%><br>
    Office version: <%=target_info[:office]%>
    |

    return template, binding()
  end

  def on_request_exploit(cli, request, target_info)
    send_exploit_html(cli, exploit_template(target_info))
  end

end

JavaScript Obfuscation

BrowserExploitServer relies on the JSObfu mixin to support JavaScript obfuscation. When you're writing JavaScript, you should always write it like this:

js = js_obfuscate(your_code)

The #js_obfuscate will return a Rex::Exploitation::JSObfu object. To get the obfuscated JavaScript, call the #to_s method:

js.to_s

If you need to access an obfuscated symbol name, you can use then #sym method:

# Get the obfuscated version of function name test()
var_name = js.sym('test')

Note that by default, even though your module is calling the #js_obfuscate method, obfuscation will not kick in unless the user sets the JsObfuscate datastore option. This option is an OptInt, which allows you to set the number of times to obfuscate (default is 0).

If your BES-based exploit does not want obfuscation at all, always make sure you call the #deregister_options and remove the JsObfuscate option. Like this:

deregister_options('JsObfuscate')

To learn more about Metasploit's JavaScript obfuscation capabilities, please read How to obfuscate JavaScript in Metasploit.

Related Articles: